// ----------------------------------------------------------------------
// @file: NewfindHelper.cc
// @author: Fabio Luchetti - CERN
// ----------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2020 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 "console/ConsoleMain.hh"
#include "console/commands/helpers/NewfindHelper.hh"
#include "common/StringTokenizer.hh"
#include "common/StringConversion.hh"
#include "XrdPosix/XrdPosixXrootd.hh"
#include "XrdOuc/XrdOucEnv.hh"
//------------------------------------------------------------------------------
// Parse command line input
//------------------------------------------------------------------------------
bool
NewfindHelper::ParseCommand(const char* arg)
{
auto* find = mReq.mutable_find();
XrdOucString s1;
std::string token;
eos::common::StringTokenizer subtokenizer(arg);
subtokenizer.GetLine();
while ((s1 = subtokenizer.GetToken()).length() > 0 && (s1.beginswith("-"))) {
if (s1 == "-s") {
find->set_silent(true);
} else if (s1 == "-d") {
find->set_directories(true);
} else if (s1 == "-f") {
find->set_files(true);
} else if (s1 == "-0") {
find->set_files(true);
find->set_zerosizefiles(true);
} else if (s1 == "--size") {
find->set_size(true);
} else if (s1 == "--fs") {
find->set_fs(true);
} else if (s1 == "--checksum") {
find->set_checksum(true);
} else if (s1 == "--ctime") {
find->set_ctime(true);
} else if (s1 == "--mtime") {
find->set_mtime(true);
} else if (s1 == "--fid") {
find->set_fid(true);
} else if (s1 == "--nrep") {
find->set_nrep(true);
} else if (s1 == "--online") {
find->set_online(true);
} else if (s1 == "--fileinfo") {
find->set_fileinfo(true);
} else if (s1 == "--nunlink") {
find->set_nunlink(true);
} else if (s1 == "--uid") {
find->set_printuid(true);
} else if (s1 == "--gid") {
find->set_printgid(true);
} else if (s1 == "--stripediff") {
find->set_stripediff(true);
} else if (s1 == "--faultyacl") {
find->set_faultyacl(true);
} else if (s1 == "--count") {
find->set_count(true);
} else if (s1 == "--cache") {
find->set_cache(true);
} else if (s1 == "--du") {
find->set_du(true);
} else if (s1 == "--du-si") {
find->set_dusi(true);
} else if (s1 == "--du-h") {
find->set_dureadable(true);
} else if (s1 == "--hosts") {
find->set_hosts(true);
} else if (s1 == "--partition") {
find->set_partition(true);
} else if (s1 == "--childcount") {
find->set_childcount(true);
} else if (s1 == "--treecount") {
find->set_treecount(true);
} else if (s1 == "--format") {
if (!subtokenizer.NextToken(token)) {
return false;
}
find->set_format(token);
} else if (s1 == "--xurl") {
find->set_xurl(true);
// } else if (s1 == "-1") {
// find->set_onehourold(true);
} else if (s1 == "-b") {
find->set_balance(true);
} else if (s1 == "-g") {
find->set_mixedgroups(true);
} else if (s1 == "-uid") {
find->set_searchuid(true);
if (!subtokenizer.NextToken(token)) {
return false;
}
try {
find->set_uid(std::stoul(token));
} catch (std::invalid_argument& error) {
return false;
}
} else if (s1 == "-nuid") {
find->set_searchnotuid(true);
if (!subtokenizer.NextToken(token)) {
return false;
}
try {
find->set_notuid(std::stoul(token));
} catch (std::invalid_argument& error) {
return false;
}
} else if (s1 == "-gid") {
find->set_searchgid(true);
if (!subtokenizer.NextToken(token)) {
return false;
}
try {
find->set_gid(std::stoul(token));
} catch (std::invalid_argument& error) {
return false;
}
} else if (s1 == "-ngid") {
find->set_searchnotgid(true);
if (!subtokenizer.NextToken(token)) {
return false;
}
try {
find->set_notgid(std::stoul(token));
} catch (std::invalid_argument& error) {
return false;
}
} else if (s1 == "-flag") {
find->set_searchpermission(true);
if (!subtokenizer.NextToken(token)) {
return false;
}
if (token.length() != 3 ||
token.find_first_not_of("01234567") != std::string::npos) {
return false;
}
find->set_permission(token);
} else if (s1 == "-nflag") {
find->set_searchnotpermission(true);
if (!subtokenizer.NextToken(token)) {
return false;
}
if (token.length() != 3 ||
token.find_first_not_of("01234567") != std::string::npos) {
return false;
}
find->set_notpermission(token);
} else if (s1 == "-x") {
if (!subtokenizer.NextToken(token)) {
return false;
}
if (token.length() > 0 && token.find('=') != std::string::npos &&
token.find('&') == std::string::npos) {
auto key = token;
auto value = token;
key.erase(token.find('='));
value.erase(0, token.find('=') + 1);
find->set_attributekey(std::move(key));
find->set_attributevalue(std::move(value));
} else {
return false;
}
} else if (s1 == "--maxdepth") {
if (!subtokenizer.NextToken(token)) {
return false;
}
if (token.length() > 0) {
try {
find->set_maxdepth(std::stoul(token));
} catch (std::invalid_argument& error) {
return false;
}
} else {
return false;
}
} else if (s1 == "--purge") {
std::string versions = subtokenizer.GetToken();
if (versions.length() > 0) {
try {
std::stoul(versions);
} catch (std::logic_error& err) {
if (versions != "atomic") {
return false;
}
}
find->set_purge(versions);
} else {
return false;
}
// @todo drop "-name" sometime later
} else if (s1 == "--name" || s1 == "-name") {
std::string filematch = subtokenizer.GetToken();
if (filematch.length() > 0) {
find->set_name(std::move(filematch));
} else {
return false;
}
} else if (s1 == "--layoutstripes") {
std::string stripes = subtokenizer.GetToken();
if (stripes.length() > 0) {
find->set_dolayoutstripes(true);
find->set_layoutstripes(std::stoul(stripes));
} else {
return false;
}
} else if (s1 == "-p") {
std::string printkey = subtokenizer.GetToken();
if (printkey.length() > 0) {
find->set_printkey(std::move(printkey));
} else {
return false;
}
} else if ((s1 == "-ctime") || (s1 == "-mtime")) {
XrdOucString period = "";
period = subtokenizer.GetToken();
if (period.length() > 0) {
bool do_olderthan = false;
bool do_youngerthan = false;
if (period.beginswith("+")) {
do_olderthan = true;
} else if (period.beginswith("-")) {
do_youngerthan = true;
}
if ((!do_olderthan) && (!do_youngerthan)) {
return false;
}
period.erase(0, 1);
time_t now = time(NULL);
now -= (86400 * strtoul(period.c_str(), 0, 10));
char snow[1024];
snprintf(snow, sizeof(snow) - 1, "%lu", now);
if (s1 == "-ctime") {
find->set_ctime(true);
} else if (s1 == "-mtime") {
find->set_mtime(true);
}
if (do_olderthan) {
try {
find->set_olderthan(std::stoul(snow));
} catch (std::invalid_argument& error) {
return false;
}
}
if (do_youngerthan) {
try {
find->set_youngerthan(std::stoul(snow));
} catch (std::invalid_argument& error) {
return false;
}
}
} else {
return false;
}
} else {
return false;
}
}
if (s1.length() > 0) {
auto path = s1;
if (!path.endswith("/") && !path.endswith(":")) {
// if the user gave file: as a search path we shouldn't add '/'=root
path += "/";
}
path = abspath(path.c_str());
find->set_path(path.c_str());
} else {
return false;
}
return true;
}
int
NewfindHelper::FindXroot(std::string path)
{
XrdPosixXrootd Xroot;
if (path.rfind('/') != path.length() - 1) {
if (path.rfind(':') != path.length() - 1) {
// if the user gave file: as a search path we shouldn't add '/'=root
path += "/";
}
}
bool XRootD = path.find("root:") == 0;
std::vector< std::vector > found_dirs;
std::map > found;
XrdOucString protocol;
XrdOucString hostport;
XrdOucString sPath;
if (path == "/") {
std::cerr << "error: I won't do a find on '/'" << std::endl;
return EINVAL;
}
const char* v = nullptr;
if (!(v = eos::common::StringConversion::ParseUrl(path.c_str(), protocol,
hostport))) {
return EINVAL;
}
sPath = v;
std::string Path = v;
if (sPath == "" && (protocol == "file")) {
sPath = getenv("PWD");
Path = getenv("PWD");
if (!sPath.endswith("/")) {
sPath += "/";
Path += "/";
}
}
found_dirs.resize(1);
found_dirs[0].resize(1);
found_dirs[0][0] = Path.c_str();
int deepness = 0;
do {
struct stat buf;
found_dirs.resize(deepness + 2);
// loop over all directories in that deepness
for (unsigned int i = 0; i < found_dirs[deepness].size(); i++) {
Path = found_dirs[deepness][i].c_str();
XrdOucString url = "";
eos::common::StringConversion::CreateUrl(protocol.c_str(), hostport.c_str(),
Path.c_str(), url);
int rstat = 0;
rstat = (XRootD) ? XrdPosixXrootd::Stat(url.c_str(), &buf) : stat(url.c_str(),
&buf);
if (rstat == 0) {
//
if (S_ISDIR(buf.st_mode)) {
// add all children
DIR* dir = (XRootD) ? XrdPosixXrootd::Opendir(url.c_str()) : opendir(
url.c_str());
if (dir != nullptr) {
struct dirent* entry;
while ((entry = (XRootD) ? XrdPosixXrootd::Readdir(dir) : readdir(dir))) {
XrdOucString curl = "";
XrdOucString cpath = Path.c_str();
cpath += entry->d_name;
if ((!strcmp(entry->d_name, ".")) || (!strcmp(entry->d_name, ".."))) {
continue; // skip . and .. directories
}
eos::common::StringConversion::CreateUrl(protocol.c_str(), hostport.c_str(),
cpath.c_str(), curl);
if (!((XRootD) ? XrdPosixXrootd::Stat(curl.c_str(), &buf) : stat(curl.c_str(),
&buf))) {
if (S_ISDIR(buf.st_mode)) {
curl += "/";
cpath += "/";
found_dirs[deepness + 1].push_back(cpath.c_str());
(void) found[curl.c_str()].size();
} else {
found[url.c_str()].insert(entry->d_name);
}
}
}
(XRootD) ? XrdPosixXrootd::Closedir(dir) : closedir(dir);
}
}
}
}
deepness++;
} while (found_dirs[deepness].size());
for (const auto& it : found) {
std::cout << it.first << std::endl;
for (const auto& sit : it.second) {
std::cout << it.first << sit << std::endl;
}
}
return 0;
}
int
NewfindHelper::FindAs3(std::string path)
{
// ----------------------------------------------------------------
// this is nightmare code because of a missing proper CLI for S3
// ----------------------------------------------------------------
XrdOucString hostport;
XrdOucString protocol;
int rc = system("which s3 >&/dev/null");
if (WEXITSTATUS(rc)) {
std::cerr <<
"error: you miss the executable provided by libs3 in your PATH" <<
std::endl;
exit(-1);
}
if (path.rfind('/') == path.length()) {
path.erase(path.length() - 1);
}
XrdOucString sPath = path.c_str();
XrdOucString sOpaque;
int qpos = 0;
if ((qpos = sPath.find("?")) != STR_NPOS) {
sOpaque.assign(sPath, qpos + 1);
sPath.erase(qpos);
}
XrdOucString fPath = eos::common::StringConversion::ParseUrl(sPath.c_str(),
protocol, hostport);
XrdOucEnv env(sOpaque.c_str());
if (env.Get("s3.key")) {
setenv("S3_SECRET_ACCESS_KEY", env.Get("s3.key"), 1);
}
if (env.Get("s3.id")) {
setenv("S3_ACCESS_KEY_ID", env.Get("s3.id"), 1);
}
// Apply the ROOT compatability environment variables
const char* cstr = getenv("S3_ACCESS_KEY");
if (cstr) {
setenv("S3_SECRET_ACCESS_KEY", cstr, 1);
}
cstr = getenv("S3_ACESSS_ID");
if (cstr) {
setenv("S3_ACCESS_KEY_ID", cstr, 1);
}
// check that the environment is set
if (!getenv("S3_ACCESS_KEY_ID") ||
!getenv("S3_HOSTNAME") ||
!getenv("S3_SECRET_ACCESS_KEY")) {
std::cerr <<
"error: you have to set the S3 environment variables S3_ACCESS_KEY_ID | S3_ACCESS_ID, S3_HOSTNAME (or use a URI), S3_SECRET_ACCESS_KEY | S3_ACCESS_KEY"
<< std::endl;
return EINVAL;
}
XrdOucString s3env;
s3env = "env S3_ACCESS_KEY_ID=";
s3env += getenv("S3_ACCESS_KEY_ID");
s3env += " S3_HOSTNAME=";
s3env += getenv("S3_HOSTNAME");
s3env += " S3_SECRET_ACCESS_KEY=";
s3env += getenv("S3_SECRET_ACCESS_KEY");
XrdOucString cmd = "bash -c \"";
cmd += s3env;
cmd += " s3 list ";
// extract bucket from path
int bpos = fPath.find("/");
XrdOucString bucket;
if (bpos != STR_NPOS) {
bucket.assign(fPath, 0, bpos - 1);
} else {
bucket = fPath.c_str();
}
XrdOucString match;
if (bpos != STR_NPOS) {
match.assign(fPath, bpos + 1);
} else {
match = "";
}
if ((!bucket.length()) || (bucket.find("*") != STR_NPOS)) {
std::cerr << "error: no bucket specified or wildcard in bucket name!" <<
std::endl;
return EINVAL;
}
cmd += bucket.c_str();
cmd += " | awk '{print \\$1}' ";
if (match.length()) {
if (match.endswith("*")) {
match.erase(match.length() - 1);
match.insert("^", 0);
}
if (match.beginswith("*")) {
match.erase(0, 1);
match += "$";
}
cmd += " | egrep '";
cmd += match.c_str();
cmd += "'";
}
cmd += " | grep -v 'Bucket' | grep -v '\\-\\-\\-\\-\\-\\-\\-\\-\\-\\-' | grep -v 'Key' | awk -v prefix=";
cmd += "'";
cmd += bucket.c_str();
cmd += "' ";
cmd += "'{print \\\"as3:\\\"prefix\\\"/\\\"\\$1}'";
cmd += "\"";
rc = system(cmd.c_str());
if (WEXITSTATUS(rc)) {
std::cerr << "error: failed to run " << cmd << std::endl;
return rc;
}
return 0;
}