//------------------------------------------------------------------------------
// File: com_proto_register.cc
// Author: Andreas-Joachim Peters - CERN
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2022 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 "common/StringTokenizer.hh"
#include "common/Path.hh"
#include "common/Timing.hh"
#include "console/ConsoleMain.hh"
#include "console/commands/ICmdHelper.hh"
int com_protoregister(char*);
void com_register_help();
//------------------------------------------------------------------------------
//! Class RegisterHelper
//------------------------------------------------------------------------------
class RegisterHelper: public ICmdHelper
{
public:
//----------------------------------------------------------------------------
//! Constructor
//!
//! @param opts global options
//----------------------------------------------------------------------------
RegisterHelper(const GlobalOptions& opts):
ICmdHelper(opts)
{
mIsAdmin = true;
}
//----------------------------------------------------------------------------
//! Destructor
//----------------------------------------------------------------------------
~RegisterHelper() override = default;
//----------------------------------------------------------------------------
//! Parse command line input
//!
//! @param arg input
//!
//! @return true if successful, otherwise false
//----------------------------------------------------------------------------
bool ParseCommand(const char* arg) override;
};
bool
RegisterHelper::ParseCommand(const char* arg)
{
XrdOucString option;
eos::console::FileRegisterProto* reg = mReq.mutable_record();
eos::common::StringTokenizer tokenizer(arg);
tokenizer.GetLine();
do {
XrdOucString s = tokenizer.GetToken();
if (s == "-u") {
reg->set_update(true);
s = tokenizer.GetToken();
}
if (s.length()) {
std::string param = s.c_str();
if (param.substr(0,4) == "uid=") {
if (eos::common::StringConversion::IsDecimalNumber(param.substr(4))) {
reg->mutable_owner()->set_uid(std::stoi(param.substr(4).c_str(),0,10));
} else {
reg->mutable_owner()->set_username(param.substr(4));
}
} else if (param.substr(0,4) == "gid=") {
if (eos::common::StringConversion::IsDecimalNumber(param.substr(4))) {
reg->mutable_owner()->set_gid(std::stoi(param.substr(4).c_str(),0,10));
} else {
reg->mutable_owner()->set_groupname(param.substr(4));
}
} else if (param.substr(0,5) == "size=") {
if (eos::common::StringConversion::IsDecimalNumber(param.substr(5))) {
reg->set_size(std::stoull(param.substr(5).c_str()));
} else {
return false;
}
} else if (param.substr(0,5) == "path=") {
std::string path = param.substr(5);
path = eos::common::StringConversion::UnQuote(path);
if (path.empty()) {
return false;
}
if (path.front() != '/') {
return false;
}
reg->set_path(path);
} else if (param.substr(0,6) == "xattr=") {
std::string key,value;
std::string kv = eos::common::StringConversion::UnQuote(param.substr(6));
eos::common::StringConversion::SplitKeyValue(kv, key, value, "=");
value = eos::common::StringConversion::UnQuote(value);
if (key.length()) {
(*reg->mutable_attr())[key] = value;
}
} else if (param.substr(0,6) == "ctime=") {
std::string t = param.substr(6);
struct timespec ts;
if (eos::common::Timing::Timespec_from_TimespecStr(t,ts)) {
return false;
}
reg->mutable_ctime()->set_sec(ts.tv_sec);
reg->mutable_ctime()->set_nsec(ts.tv_nsec);
} else if (param.substr(0,6) == "atime=") {
std::string t = param.substr(6);
struct timespec ts;
if (eos::common::Timing::Timespec_from_TimespecStr(t,ts)) {
return false;
}
reg->mutable_atime()->set_sec(ts.tv_sec);
reg->mutable_atime()->set_nsec(ts.tv_nsec);
} else if (param.substr(0,13) == "atimeifnewer=") {
std::string t = param.substr(13);
struct timespec ts;
if (eos::common::Timing::Timespec_from_TimespecStr(t,ts)) {
return false;
}
reg->mutable_atime()->set_sec(ts.tv_sec);
reg->mutable_atime()->set_nsec(ts.tv_nsec);
reg->set_atimeifnewer(true);
} else if (param.substr(0,6) == "btime=") {
std::string t = param.substr(6);
struct timespec ts;
if (eos::common::Timing::Timespec_from_TimespecStr(t,ts)) {
return false;
}
reg->mutable_btime()->set_sec(ts.tv_sec);
reg->mutable_btime()->set_nsec(ts.tv_nsec);
} else if (param.substr(0,6) == "mtime=") {
std::string t = param.substr(6);
struct timespec ts;
if (eos::common::Timing::Timespec_from_TimespecStr(t,ts)) {
return false;
}
reg->mutable_mtime()->set_sec(ts.tv_sec);
reg->mutable_mtime()->set_nsec(ts.tv_nsec);
} else if (param.substr(0,5) == "mode=") {
if (eos::common::StringConversion::IsDecimalNumber(param.substr(5))) {
reg->set_mode(std::stoi(param.substr(5).c_str(),0, 8));
} else {
return false;
}
} else if (param.substr(0,9) == "location=") {
reg->mutable_locations()->Add(std::stoi(param.substr(9).c_str(),0,10));
} else if (param.substr(0,9) == "layoutid=") {
reg->set_layoutid(std::stoi(param.substr(9).c_str(),0,10));
fprintf(stderr,"layoutid:%d %s\n", std::stoi(param.substr(9).c_str(),0,10), param.substr(9).c_str());
} else if (param.substr(0,9) == "checksum=") {
reg->set_checksum(param.substr(9));
} else {
std::string path = param;
path = eos::common::StringConversion::UnQuote(path);
if (path.empty()) {
return false;
}
if (path.front() != '/') {
return false;
}
reg->set_path(path);
}
} else {
break;
}
} while (true);
if (reg->path().empty()) {
return false;
}
return true;
}
//------------------------------------------------------------------------------
// Register command entry point
//------------------------------------------------------------------------------
int com_protoregister(char* arg)
{
if (wants_help(arg)) {
com_register_help();
global_retc = EINVAL;
return EINVAL;
}
RegisterHelper reg(gGlobalOpts);
if (!reg.ParseCommand(arg)) {
com_register_help();
global_retc = EINVAL;
return EINVAL;
}
global_retc = reg.Execute(true, true);
return global_retc;
}
void com_register_help()
{
std::ostringstream oss;
oss << "Usage: register [-u] {tag1,tag2,tag3...}"
<< std::endl;
oss << " : when called without the -u flag the parent has to exist while the basename should not exist" << std::endl;
oss << " -u : if the file exists this will update all the provided meta-data of a file" << std::endl;
oss << std::endl;
oss << " tagN is optional, but can be one or many of: "
<< std::endl;
oss << " size=100" << std::endl;
oss << " uid=101 | username=foo" << std::endl;
oss << " gid=102 | username=bar" << std::endl;
oss << " checksum=abcdabcd" << std::endl;
oss << " layoutid=00100112" << std::endl;
oss << " location=1 location=2 ..." << std::endl;
oss << " mode=777" << std::endl;
oss << " btime=1670334863.101232" << std::endl;
oss << " atime=1670334863.101232" << std::endl;
oss << " ctime=1670334863.110123" << std::endl;
oss << " mtime=1670334863.11234d" << std::endl;
oss << " attr=\"sys.acl=u:100:rwx\"" << std::endl;
oss << " attr=\"user.md=private\"" << std::endl;
oss << " path=\"/eos/newfile\" # can be used instead of the regular path argument of the path" << std::endl;
oss << " atimeifnewer=1670334863.101233 # only update if this atime is newer than the existing one!" << std::endl;
std::cerr << oss.str() << std::endl;
}