//------------------------------------------------------------------------------
// File: FsHelper.cc
// Author: Jozsef Makai - CERN
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2018 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#include "FsHelper.hh"
#include "common/StringTokenizer.hh"
#include
#include
//------------------------------------------------------------------------------
// Parse command line input
//------------------------------------------------------------------------------
bool
FsHelper::ParseCommand(const char* arg)
{
const char* option;
std::string soption;
eos::console::FsProto* fs = mReq.mutable_fs();
eos::common::StringTokenizer tokenizer(arg);
tokenizer.GetLine();
option = tokenizer.GetToken();
std::string cmd = (option ? option : "");
if (cmd == "add") {
eos::console::FsProto_AddProto* add = fs->mutable_add();
if (!(option = tokenizer.GetToken())) {
return false;
} else {
soption = option;
if ((soption == "-m") || (soption == "--manual")) {
add->set_manual(true);
// Parse fsid
if (!(option = tokenizer.GetToken())) {
std::cerr << "error: manual flag needs to be followed by fsid"
<< std::endl;
return false;
}
soption = option;
try {
uint64_t fsid = std::stoull(soption);
add->set_fsid(fsid);
} catch (const std::exception& e) {
std::cerr << "error: fsid needs to be numeric" << std::endl;
return false;
}
}
// Advance token if manual option was set
if (add->manual() && !(option = tokenizer.GetToken())) {
std::cerr << "error: missing uuid" << std::endl;
return false;
}
// Parse uuid
add->set_uuid(option);
// Parse node queue or host:port
if (!(option = tokenizer.GetToken())) {
std::cerr << "error: missing node-queue or host" << std::endl;
return false;
}
soption = option;
if (!soption.empty() && (soption[0] == '/')) {
add->set_nodequeue(soption);
} else {
add->set_hostport(soption);
}
// Parse mountpoint
if (!(option = tokenizer.GetToken())) {
std::cerr << "error: missing mountpoint" << std::endl;
return false;
}
add->set_mountpoint(option);
// Parse scheduling group
if (!(option = tokenizer.GetToken())) {
// Set "default" scheduling group
add->set_schedgroup("default");
add->set_status("off");
} else {
add->set_schedgroup(option);
// Parse status
if (!(option = tokenizer.GetToken())) {
// Default status is "off"
add->set_status("off");
} else {
add->set_status(option);
}
}
// Parse sharedfs name
if ((option = tokenizer.GetToken())) {
add->set_sharedfs(option);
}
}
} else if (cmd == "boot") {
using eos::console::FsProto_BootProto;
FsProto_BootProto* boot = fs->mutable_boot();
if (!(option = tokenizer.GetToken())) {
return false;
} else {
soption = option;
// Parse * or node-queue identifier
if (soption == "*") {
boot->set_nodequeue("*");
} else if (soption[0] == '/') {
boot->set_nodequeue(soption);
} else {
// Parse or
bool isUuid =
soption.find_first_not_of("0123456789") != std::string::npos;
if (isUuid) {
boot->set_uuid(soption);
} else {
try {
uint64_t fsid = std::stoull(soption);
boot->set_fsid(fsid);
} catch (const std::exception& e) {
std::cerr << "error: fsid needs to be numeric" << std::endl;
return false;
}
}
}
if ((option = tokenizer.GetToken())) {
soption = option;
if (soption != "--syncmgm") {
std::cerr << "error: unknown option: " << soption << std::endl;
return false;
} else {
boot->set_syncmgm(true);
}
}
}
} else if (cmd == "clone") {
auto* clone = fs->mutable_clone();
if (!(option = tokenizer.GetToken())) {
return false;
} else {
soption = option;
try {
auto sourceid = std::stoull(soption);
clone->set_sourceid(sourceid);
} catch (const std::exception& e) {
std::cerr << "error: fsid needs to be numeric" << std::endl;
return false;
}
}
if (!(option = tokenizer.GetToken())) {
return false;
} else {
soption = option;
try {
auto targetid = std::stoull(soption);
clone->set_targetid(targetid);
} catch (const std::exception& e) {
std::cerr << "error: fsid needs to be numeric" << std::endl;
return false;
}
}
} else if (cmd == "compare") {
auto* compare = fs->mutable_compare();
if (!(option = tokenizer.GetToken())) {
return false;
} else {
soption = option;
try {
auto sourceid = std::stoull(soption);
compare->set_sourceid(sourceid);
} catch (const std::exception& e) {
std::cerr << "error: fsid needs to be numeric" << std::endl;
return false;
}
}
if (!(option = tokenizer.GetToken())) {
return false;
} else {
soption = option;
try {
auto targetid = std::stoull(soption);
compare->set_targetid(targetid);
} catch (const std::exception& e) {
std::cerr << "error: fsid needs to be numeric" << std::endl;
return false;
}
}
} else if (cmd == "config") {
using eos::console::FsProto_ConfigProto;
FsProto_ConfigProto* config = fs->mutable_config();
if (!(option = tokenizer.GetToken())) {
return false;
} else {
soption = option;
// Parse
bool not_numeric = (soption.find_first_not_of("0123456789") !=
std::string::npos);
if (not_numeric) {
std::cerr << "error: fsid needs to be numeric" << std::endl;
return false;
} else {
try {
uint64_t fsid = std::stoull(soption);
config->set_fsid(fsid);
} catch (const std::exception& e) {
std::cerr << "error: fsid needs to be numeric" << std::endl;
return false;
}
}
// Parse key=value
if (!(option = tokenizer.GetToken())) {
std::cerr << "error: configuration must be specified in ="
" format" << std::endl;
return false;
}
soption = option;
auto pos = soption.find('=');
if (pos == std::string::npos) {
std::cerr << "error: configuration must be specified in ="
" format" << std::endl;
return false;
}
config->set_key(soption.substr(0, pos));
config->set_value(soption.substr(pos + 1));
}
} else if (cmd == "dropdeletion") {
using eos::console::FsProto_DropDeletionProto;
FsProto_DropDeletionProto* dropdel = fs->mutable_dropdel();
if (!(option = tokenizer.GetToken())) {
return false;
} else {
soption = option;
try {
uint64_t fsid = std::stoull(soption);
dropdel->set_fsid(fsid);
} catch (const std::exception& e) {
std::cerr << "error: fsid needs to be numeric" << std::endl;
return false;
}
}
} else if (cmd == "dropghosts") {
using eos::console::FsProto_DropGhostsProto;
FsProto_DropGhostsProto* dropghosts = fs->mutable_dropghosts();
if (!(option = tokenizer.GetToken())) {
return false;
} else {
soption = option;
try {
uint64_t fsid = std::stoull(soption);
dropghosts->set_fsid(fsid);
} catch (const std::exception& e) {
std::cerr << "error: fsid needs to be numeric" << std::endl;
return false;
}
// Parse optional list of explicit fids
if (tokenizer.NextToken(soption)) {
if (soption != "--fxid") {
std::cerr << "error: unknown option \"" << soption << "\"" << std::endl;
return false;
}
// Now parse the list of fids
while (tokenizer.NextToken(soption)) {
try {
dropghosts->add_fids(std::stoull(soption, 0, 16));
} catch (std::exception& e) {
std::cerr << "error: fxid needs to be (hex) numeric" << std::endl;
return false;
}
}
}
}
} else if (cmd == "dropfiles") {
using eos::console::FsProto_DropFilesProto;
FsProto_DropFilesProto* dropfiles = fs->mutable_dropfiles();
if (!(option = tokenizer.GetToken())) {
return false;
} else {
soption = option;
try {
uint64_t fsid = std::stoull(soption);
dropfiles->set_fsid(fsid);
} catch (const std::exception& e) {
std::cerr << "error: fsid needs to be numeric" << std::endl;
return false;
}
// Parse -f optional flag
if ((option = tokenizer.GetToken())) {
soption = option;
if (soption != "-f") {
std::cerr << "error: unknown option: " << soption << std::endl;
return false;
}
dropfiles->set_force(true);
}
mNeedsConfirmation = true;
}
} else if (cmd == "dumpmd") {
using eos::console::FsProto_DumpMdProto;
FsProto_DumpMdProto* dumpmd = fs->mutable_dumpmd();
if (!(option = tokenizer.GetToken())) {
return false;
} else {
soption = option;
try {
uint64_t fsid = std::stoull(soption);
dumpmd->set_fsid(fsid);
} catch (const std::exception& e) {
std::cerr << "error: fsid needs to be numeric" << std::endl;
return false;
}
// Parse any optional flags
if ((option = tokenizer.GetToken())) {
while (true) {
soption = option;
if (soption == "--fid") {
dumpmd->set_showfid(true);
} else if (soption == "--fxid") {
dumpmd->set_showfxid(true);
} else if (soption == "--path") {
dumpmd->set_showpath(true);
} else if (soption == "--size") {
dumpmd->set_showsize(true);
} else if (soption == "-s") {
mIsSilent = true;
} else if (soption == "-m") {
dumpmd->set_display(FsProto_DumpMdProto::MONITOR);
}
if (!(option = tokenizer.GetToken())) {
break;
}
}
}
}
} else if (cmd == "mv") {
using eos::console::FsProto_MvProto;
FsProto_MvProto* mv = fs->mutable_mv();
if (!(option = tokenizer.GetToken())) {
return false;
} else {
soption = option;
if (soption == "--force") {
if (!(option = tokenizer.GetToken())) {
return false;
}
mv->set_force(true);
}
soption = option;
mv->set_src(soption);
if (!(option = tokenizer.GetToken())) {
return false;
}
soption = option;
mv->set_dst(soption);
}
} else if (cmd == "ls") {
using eos::console::FsProto_LsProto;
FsProto_LsProto* ls = fs->mutable_ls();
if ((option = tokenizer.GetToken())) {
int exclusive_opt = 0;
while (true) {
soption = option;
if (soption == "-m") {
ls->set_display(FsProto_LsProto::MONITOR);
++exclusive_opt;
} else if (soption == "-l") {
ls->set_display(FsProto_LsProto::LONG);
++exclusive_opt;
} else if (soption == "-e") {
ls->set_display(FsProto_LsProto::ERROR);
++exclusive_opt;
} else if (soption == "--io") {
ls->set_display(FsProto_LsProto::IO);
++exclusive_opt;
} else if (soption == "--fsck") {
ls->set_display(FsProto_LsProto::FSCK);
++exclusive_opt;
} else if ((soption == "-d") || (soption == "--drain")) {
ls->set_display(FsProto_LsProto::DRAIN);
++exclusive_opt;
} else if ((soption == "-D") || (soption == "--drain_jobs")) {
ls->set_display(FsProto_LsProto::RUNNING_DRAIN_JOBS);
++exclusive_opt;
} else if ((soption == "-F") || (soption == "--failed_drain_jobs")) {
ls->set_display(FsProto_LsProto::FAILED_DRAIN_JOBS);
++exclusive_opt;
} else if (soption == "-s") {
mIsSilent = true;
} else if ((soption == "-b") || (soption == "--brief")) {
ls->set_brief(true);
} else {
// This needs to be the matchlist
ls->set_matchlist(soption);
}
if (exclusive_opt >= 2) {
std::cerr << "error: two exclusive options in the same command"
<< std::endl;
return false;
}
if (!(option = tokenizer.GetToken())) {
break;
}
}
}
} else if (cmd == "rm") {
using eos::console::FsProto_RmProto;
FsProto_RmProto* rm = fs->mutable_rm();
if (!(option = tokenizer.GetToken())) {
return false;
} else {
soption = option;
// Parse nodequeue specification
if ((soption.find("/eos/") == 0) &&
(soption.find(':') != std::string::npos) &&
(soption.find('.') != std::string::npos)) {
// Check if it ends in /fst, if not append it
std::string search = "/fst";
if (soption.rfind(search) != soption.length() - search.length()) {
soption += "/fst";
}
// Parse the mountpoint and remove any ending /
std::string mountpoint;
if (!(option = tokenizer.GetToken())) {
std::cerr << "error: no mountpoint specified" << std::endl;
return false;
}
mountpoint = option;
if (*mountpoint.rbegin() == '/') {
mountpoint.pop_back();
}
soption += mountpoint;
rm->set_nodequeue(soption);
} else {
// Parse mountpoint and append any required info
if (soption[0] == '/') {
char hostname[255];
if (gethostname(hostname, sizeof(hostname)) == -1) {
std::cerr << "error: failed to get local hostname" << std::endl;
return false;
}
std::ostringstream oss;
oss << "/eos/" << hostname << ":1095/fst" << soption;
rm->set_nodequeue(oss.str());
} else if (std::find_if(soption.begin(), soption.end(),
[](char c) {
return std::isalpha(c);
})
!= soption.end()) {
// This contains at least one alphabetic char therefore it must be
// a hostname, parse the mountpoint and construct the node-queue
std::string mountpoint;
if (!(option = tokenizer.GetToken())) {
std::cerr << "error: mountpoint missing" << std::endl;
return false;
}
mountpoint = option;
if (mountpoint.empty() || mountpoint[0] != '/') {
std::cerr << "error: invalid mountpoint" << std::endl;
return false;
}
if (*mountpoint.rbegin() == '/') {
mountpoint.pop_back();
}
bool has_port = false;
auto pos = soption.find(':');
if ((pos != std::string::npos) && (pos < soption.length())) {
has_port = true;
}
std::ostringstream oss;
oss << "/eos/" << soption;
if (!has_port) {
oss << ":1095";
}
oss << "/fst" << mountpoint;
rm->set_nodequeue(oss.str());
}
else {
// This needs to be an fsid
try {
uint64_t fsid = std::stoull(soption);
rm->set_fsid(fsid);
} catch (const std::exception& e) {
return false;
}
}
}
}
} else if (cmd == "status") {
using eos::console::FsProto_StatusProto;
FsProto_StatusProto* status = fs->mutable_status();
if (!(option = tokenizer.GetToken())) {
return false;
} else {
while (true) {
soption = option;
std::ostringstream oss;
if (soption == "-l") {
status->set_longformat(true);
} else if (soption == "-r") {
status->set_riskassessment(true);
} else {
// This is a hostname specification
if ((soption.find('.') != std::string::npos) &&
(soption.find('/') == std::string::npos)) {
// Check for mountpoint
if (!(option = tokenizer.GetToken()) || (option[0] != '/')) {
std::cerr << "error: no mountpoint specified" << std::endl;
return false;
}
oss << "/eos/" << soption << "/fst" << option;
status->set_nodequeue(oss.str());
} else if (soption[0] == '/') {
// This is a mountpoint append the local hostname
char hostname[255];
if (gethostname(hostname, sizeof(hostname)) == -1) {
std::cerr << "error: failed to get local hostname" << std::endl;
return false;
}
oss << "/eos/" << hostname << ":1095/fst" << soption;
status->set_nodequeue(oss.str());
} else if (std::isalpha(soption[0])) {
// This is a hostname specification, check for mountpoint
if (!(option = tokenizer.GetToken())) {
std::cerr << "error: no mountpoint specified" << std::endl;
return false;
}
oss << "/eos/" << soption << "/fst" << option;
status->set_nodequeue(oss.str());
} else {
// This needs to be a fsid
try {
uint64_t fsid = std::stoull(soption);
status->set_fsid(fsid);
} catch (const std::exception& e) {
return false;
}
}
}
if (!(option = tokenizer.GetToken())) {
break;
}
}
if ((status->fsid() == 0) && (status->nodequeue().empty())) {
std::cerr << "error: fsid or host/mountpoint needs to be specified"
<< std::endl;
return false;
}
}
} else {
return false;
}
return true;
}