// ---------------------------------------------------------------------- // File: Event.cc // Author: Andreas-Joachim Peters - 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 "common/Logging.hh" #include "common/Mapping.hh" #include "common/SecEntity.hh" #include "namespace/interface/IFileMD.hh" #include "namespace/interface/IContainerMD.hh" #include "mgm/Stat.hh" #include "mgm/XrdMgmOfs.hh" #include "mgm/Macros.hh" #include "mgm/Acl.hh" #include "mgm/Workflow.hh" #include "mgm/FsView.hh" #include //---------------------------------------------------------------------------- // Trigger an event //---------------------------------------------------------------------------- int XrdMgmOfs::Event(const char* path, const char* ininfo, XrdOucEnv& env, XrdOucErrInfo& error, eos::common::VirtualIdentity& vid, const XrdSecEntity* client) { static const char* epname = "Event"; char* auid = env.Get("mgm.ruid"); char* agid = env.Get("mgm.rgid"); char* asec = env.Get("mgm.sec"); char* alogid = env.Get("mgm.logid"); char* spath = env.Get("mgm.path"); char* afid = env.Get("mgm.fid"); char* aevent = env.Get("mgm.event"); char* aworkflow = env.Get("mgm.workflow"); char* errmsg = env.Get("mgm.errmsg"); eos::common::VirtualIdentity localVid = eos::common::VirtualIdentity::Nobody(); int errc; if (auid) { localVid.uid = strtoul(auid, 0, 10); localVid.uid_string = eos::common::Mapping::UidToUserName(localVid.uid, errc); } if (agid) { localVid.gid = strtoul(agid, 0, 10); localVid.gid_string = eos::common::Mapping::GidToGroupName(localVid.gid, errc); localVid.allowed_gids = vid.allowed_gids; } if (asec) { std::map secmap = eos::common::SecEntity::KeyToMap(std::string(asec)); localVid.prot = secmap["prot"].c_str(); localVid.name = secmap["name"].c_str(); localVid.host = secmap["host"]; localVid.grps = secmap["grps"]; localVid.app = secmap["app"]; } if (alogid) { tlLogId.SetLogId(alogid, error.getErrUser()); } eos_thread_debug("vid.prot=%s, vid.uid=%u, vid.gid=%u", vid.prot.c_str(), vid.uid, vid.gid); eos_thread_debug("local.prot=%s, local.uid=%u, local.gid=%u", localVid.prot.c_str(), localVid.uid, localVid.gid); // Assuming that all workflow actions accept for prepare can modify a file, // check that we have either write or prepare permission as necessary on path bool isPrepare = (aevent != nullptr && std::string(aevent).find("prepare") != std::string::npos); const int mode = isPrepare ? P_OK : W_OK; if (vid.prot != "sss" && gOFS->_access(spath, mode, error, localVid, "")) { const char* emsg = isPrepare ? "event - you don't have prepare permissions [EPERM]" : "event - you don't have write permission [EPERM]"; return Emsg(epname, error, EPERM, emsg, spath); } ACCESSMODE_W; MAYSTALL; MAYREDIRECT; EXEC_TIMING_BEGIN("Event"); gOFS->MgmStats.Add("Event", 0, 0, 1); if (spath && afid && aevent && aworkflow) { eos_thread_info("subcmd=event event=%s path=%s fid=%s", aevent, spath, afid); unsigned long long fid = strtoull(afid, 0, 16); std::string event = aevent; std::shared_ptr fmd; std::shared_ptr cmd; Workflow workflow; eos::IContainerMD::XAttrMap attrmap; XrdOucString lWorkflow = aworkflow; if (lWorkflow.beginswith("eos.")) { // Template workflow defined under the workflow proc directory spath = (char*) gOFS->MgmProcWorkflowPath.c_str(); fid = 0; } { eos::common::RWMutexReadLock vlock(FsView::gFsView.ViewMutex); try { if (fid) { fmd = gOFS->eosFileService->getFileMD(fid); } else { fmd = gOFS->eosView->getFile(spath); fid = fmd->getId(); } cmd = gOFS->eosDirectoryService->getContainerMD(fmd->getContainerId()); eos::IFileMD::XAttrMap xattrs = cmd->getAttributes(); for (const auto& elem : xattrs) { attrmap[elem.first] = elem.second; } // Check for attribute references if (attrmap.count("sys.attr.link")) { try { cmd = gOFS->eosView->getContainer(attrmap["sys.attr.link"]); eos::IFileMD::XAttrMap xattrs = cmd->getAttributes(); for (const auto& elem : xattrs) { if (!attrmap.count(elem.first)) { attrmap[elem.first] = elem.second; } } } catch (eos::MDException& e) { cmd.reset(); errno = e.getErrno(); eos_thread_debug("msg=\"exception\" ec=%d emsg=\"%s\"", e.getErrno(), e.getMessage().str().c_str()); } attrmap.erase("sys.attr.link"); } } catch (eos::MDException& e) { errno = e.getErrno(); eos_thread_debug("msg=\"exception\" ec=%d emsg=\"%s\"", e.getErrno(), e.getMessage().str().c_str()); } } // Load the corresponding workflow std::string strpath = spath; workflow.Init(&attrmap, strpath, fid); std::string decodedErrMessage = "trigger workflow - synchronous workflow failed"; if (errmsg != nullptr) { if (!eos::common::SymKey::Base64Decode(errmsg, decodedErrMessage)) { decodedErrMessage = ""; } } // Trigger the specified event const int rc = workflow.Trigger(event, aworkflow, localVid, ininfo, decodedErrMessage); if (rc == -1) { int envlen = 0; if (errno == ENOKEY) { // No workflow defined return Emsg(epname, error, EINVAL, "trigger workflow - no workflow defined for" " . [EINVAL]", env.Env(envlen)); } else { if (!workflow.IsSync()) { return Emsg(epname, error, EIO, "trigger workflow - internal error [EIO]", env.Env(envlen)); } else { return Emsg(epname, error, errno, decodedErrMessage.c_str(), env.Env(envlen)); } } } if (rc != 0) { std::ostringstream errStr; errStr << "complete workflow - error while executing " << event << " workflow [" << MacroStringError(rc) << "]"; if (decodedErrMessage.empty()) { return Emsg(epname, error, rc, errStr.str().c_str(), spath); } else { return Emsg(epname, error, rc, decodedErrMessage.c_str(), spath); } } } else { int envlen = 0; const char* env_string = env.Env(envlen); eos_thread_err("invalid parameters for event call: %s", env_string); return Emsg(epname, error, EINVAL, "notify - invalid parameters for event call: %s [EINVAL]", env_string); } const char* ok = "OK"; error.setErrInfo(strlen(ok) + 1, ok); EXEC_TIMING_END("Event"); return SFS_DATA; }