//------------------------------------------------------------------------------ // @file: DebugCmd.cc // @author: Fabio Luchetti - 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 "DebugCmd.hh" #include "mgm/proc/ProcInterface.hh" #include "mgm/XrdMgmOfs.hh" #include "mgm/FsView.hh" #include "mq/MessagingRealm.hh" EOSMGMNAMESPACE_BEGIN //------------------------------------------------------------------------------ // Method implementing the specific behavior of the command //------------------------------------------------------------------------------ eos::console::ReplyProto DebugCmd::ProcessRequest() noexcept { eos::console::ReplyProto reply; eos::console::DebugProto debug = mReqProto.debug(); switch (mReqProto.debug().subcmd_case()) { case eos::console::DebugProto::kGet: GetSubcmd(debug.get(), reply); break; case eos::console::DebugProto::kSet: SetSubcmd(debug.set(), reply); break; default: reply.set_retc(EINVAL); reply.set_std_err("error: not supported"); } return reply; } //------------------------------------------------------------------------------ // Execute get subcommand //------------------------------------------------------------------------------ void DebugCmd::GetSubcmd(const eos::console::DebugProto_GetProto& get, eos::console::ReplyProto& reply) { std::ostringstream std_out; eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex); eos::common::Logging& g_logging = eos::common::Logging::GetInstance(); std_out << "# ------------------------------------------------------------------------------------\n" << "# Debug log level\n" << "# ....................................................................................\n"; std::string priority = g_logging.GetPriorityString(g_logging.gPriorityLevel); std::for_each(priority.begin(), priority.end(), [](char& c) { c = ::tolower(static_cast(c)); }); std_out << "/eos/" << gOFS->HostName << ':' << gOFS->ManagerPort << "/mgm := " << priority.c_str() << std::endl; auto nodes = FsView::gFsView.mNodeView; for (auto& node : nodes) { std_out << node.first << " := " << FsView::gFsView.mNodeView[node.first]->GetConfigMember("debug.state") << std::endl; } reply.set_std_out(std_out.str()); reply.set_retc(0); } //------------------------------------------------------------------------------ // Build string that is put into a message sent to the FSTs or slaves with the // new log level //------------------------------------------------------------------------------ std::string PrepareMsg(const eos::console::DebugProto_SetProto& set) { std::string in = "mgm.cmd=debug"; if (set.debuglevel().length()) { in += "&mgm.debuglevel=" + set.debuglevel(); } if (set.nodename().length()) { in += "&mgm.nodename=" + set.nodename(); } if (set.filter().length()) { in += "&mgm.filter=" + set.filter(); } return in; } //------------------------------------------------------------------------------ // Build query string to be sent to the FSTs to change the debug level //------------------------------------------------------------------------------ std::string PrepareQuery(const eos::console::DebugProto_SetProto& set) { std::ostringstream oss; oss << "/?fst.pcmd=debug" << "&fst.debug.level=" << set.debuglevel(); if (!set.filter().empty()) { oss << "&fst.debug.filter=" << set.filter(); } return oss.str(); } //------------------------------------------------------------------------------ // Execute set subcommand //------------------------------------------------------------------------------ void DebugCmd::SetSubcmd(const eos::console::DebugProto_SetProto& set, eos::console::ReplyProto& reply) { if (mVid.uid != 0) { reply.set_std_err("error: only role 'root' can execute this command"); reply.set_retc(EPERM); return; } // Always check debug level exists first eos::common::Logging& g_logging = eos::common::Logging::GetInstance(); int debugval = g_logging.GetPriorityByString(set.debuglevel().c_str()); if (debugval < 0) { reply.set_std_err(SSTR("error: unknown log level <" + set.debuglevel() + ">")); reply.set_retc(EINVAL); return; } // Filter out several *'s ... int nstars = 0; int npos = 0; while ((npos = set.nodename().find('*', npos)) != STR_NPOS) { npos++; nstars++; } if (nstars > 1) { reply.set_std_err("error: debug level node can only contain one wildcard " "character (*)!"); reply.set_retc(EINVAL); return; } int ret_c = 0; std::ostringstream out, err; std::string body = PrepareMsg(set); std::string query = PrepareQuery(set); if ((set.nodename() == "*") || (set.nodename().empty()) || (XrdOucString(set.nodename().c_str()) == gOFS->MgmOfsQueue) || (set.nodename() == "/eos/*/mgm")) { g_logging.SetLogPriority(debugval); out << "success: log level is now <" + set.debuglevel() + '>'; eos_static_notice("msg=\"setting log level to <%s>\"", set.debuglevel().c_str()); if (set.filter().length()) { g_logging.SetFilter(set.filter().c_str()); out << " filter=" + set.filter(); eos_static_notice("msg=\"setting message logid filter to <%s>\"", set.filter().c_str()); } if (set.debuglevel() == "debug" && ((g_logging.gAllowFilter.Num() && g_logging.gAllowFilter.Find("SharedHash")) || ((g_logging.gDenyFilter.Num() == 0) || (g_logging.gDenyFilter.Find("SharedHash") == 0)))) { gOFS->ObjectManager.SetDebug(true); } else { gOFS->ObjectManager.SetDebug(false); } } if ((set.nodename() == "/eos/*/mgm") || set.nodename().empty()) { reply.set_retc(ret_c); return; } std::set endpoints = FsView::gFsView.CollectEndpoints( set.nodename()); if (endpoints.empty()) { reply.set_std_err("error: requested endpoint(s) not existing or not online"); reply.set_retc(EINVAL); return; } std::map> responses; int query_retc = gOFS->BroadcastQuery(query, endpoints, responses); if (query_retc == 0) { out.str(""); out.clear(); out << ("success: log level=" + set.debuglevel() + " on nodename=" + set.nodename() + "\n").c_str(); eos_static_notice("msg=\"forwarding log level <%s> to nodename=%s\"", set.debuglevel().c_str(), set.nodename().c_str()); } else { err << ("error: could not send log level to nodename=" + set.nodename() + "\n").c_str(); eos_static_err("msg=\"failed log level broadcast\" nodename=\"%s\"", set.nodename().c_str()); ret_c = EINVAL; } reply.set_std_out(out.str()); reply.set_std_err(err.str()); reply.set_retc(ret_c); } EOSMGMNAMESPACE_END