Commit 8a7d12f1 authored by Fabio Luchetti's avatar Fabio Luchetti

MGM/CONSOLE: Move debug command to protobuf implementation and add 'getloglevel' subcommand

parent 1cdde233
Pipeline #928504 passed with stages
in 71 minutes and 25 seconds
......@@ -62,6 +62,7 @@ add_library(
commands/com_console.cc
commands/com_cp.cc
commands/com_debug.cc
commands/com_proto_debug.cc
commands/com_file.cc
commands/com_find.cc
commands/com_fs.cc
......
......@@ -66,7 +66,8 @@ extern int com_clear(char*);
extern int com_config(char*);
extern int com_console(char*);
extern int com_cp(char*);
extern int com_debug(char*);
// extern int com_debug(char*);
extern int com_protodebug(char*);
extern int com_file(char*);
extern int com_fileinfo(char*);
extern int com_find(char*);
......@@ -137,7 +138,8 @@ COMMAND commands[] = {
{ (char*) "config", com_config, (char*) "Configuration System"},
{ (char*) "console", com_console, (char*) "Run Error Console"},
{ (char*) "cp", com_cp, (char*) "Cp command"},
{ (char*) "debug", com_debug, (char*) "Set debug level"},
// { (char*) "debug", com_debug, (char*) "Set debug level"},
{ (char*) "debug", com_protodebug, (char*) "Set debug level"},
{ (char*) "exit", com_quit, (char*) "Exit from EOS console"},
{ (char*) "file", com_file, (char*) "File Handling"},
{ (char*) "fileinfo", com_fileinfo, (char*) "File Information"},
......
......@@ -213,3 +213,20 @@ ICmdHelper::AddRouteInfo(std::string& cmd)
cmd += oss.str();
}
bool
ICmdHelper::next_token(eos::common::StringTokenizer& tokenizer,
std::string& token)
{
const char* tmp = tokenizer.GetToken();
//token = (tmp ? tmp : "");
if (!tmp) {
token = "";
return false;
} else {
token = tmp;
return true;
}
}
......@@ -63,6 +63,8 @@ public:
//----------------------------------------------------------------------------
virtual bool ParseCommand(const char* arg) = 0;
bool next_token(eos::common::StringTokenizer& tokenizer, std::string& token);
//----------------------------------------------------------------------------
//! Execute command and display any output information
//! @note When this methods is called the generic request object mReq needs
......
//------------------------------------------------------------------------------
// File: com_proto_debug.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 <http://www.gnu.org/licenses/>.*
************************************************************************/
#include "common/StringTokenizer.hh"
#include "common/Logging.hh"
#include "console/ConsoleMain.hh"
#include "console/commands/ICmdHelper.hh"
void com_debug_help();
//------------------------------------------------------------------------------
//! Class DebugHelper
//------------------------------------------------------------------------------
class DebugHelper : public ICmdHelper
{
public:
bool execute_locally = false; // 'debug this' is not forwarded
//----------------------------------------------------------------------------
//! Constructor
//----------------------------------------------------------------------------
DebugHelper() = default;
//----------------------------------------------------------------------------
//! Destructor
//----------------------------------------------------------------------------
~DebugHelper() = default;
//----------------------------------------------------------------------------
//! Parse command line input
//!
//! @param arg input
//!
//! @return true if successful, otherwise false
//----------------------------------------------------------------------------
bool ParseCommand(const char* arg) override;
};
//------------------------------------------------------------------------------
// Parse command line input
//------------------------------------------------------------------------------
bool DebugHelper::ParseCommand(const char* arg)
{
eos::console::DebugProto* debugproto = mReq.mutable_debug();
eos::common::StringTokenizer tokenizer(arg);
tokenizer.GetLine();
std::string token;
if (!next_token(tokenizer, token)) {
return false;
}
if ((token == "-h") || (token == "--help")) {
return false;
}
if (token == "get") {
eos::console::DebugProto_GetProto* get = debugproto->mutable_get();
get->set_placeholder(true);
} else if (token == "this") {
debug = !debug;
fprintf(stdout, "info: toggling shell debugmode to debug=%d\n", debug);
eos::common::Logging& g_logging = eos::common::Logging::GetInstance();
if (debug) {
g_logging.SetLogPriority(LOG_DEBUG);
} else {
g_logging.SetLogPriority(LOG_NOTICE);
}
execute_locally = true;
} else { // token (supposedly) equals one of [debug info warning notice err crit alert emerg]
eos::console::DebugProto_SetProto* set = debugproto->mutable_set();
set->set_debuglevel(token);
if (next_token(tokenizer, token)) {
if (token == "--filter") {
if (!next_token(tokenizer, token)) {
return false;
}
set->set_filter(token);
} else {
set->set_nodename(token);
if (next_token(tokenizer, token)) {
if (token != "--filter") {
return false;
} else {
if (!next_token(tokenizer, token)) {
return false;
} else {
set->set_filter(token);
}
}
}
}
}
}
return true;
}
//------------------------------------------------------------------------------
// Debug CLI
//------------------------------------------------------------------------------
int
com_protodebug(char* arg)
{
if (wants_help(arg)) {
com_debug_help();
global_retc = EINVAL;
return EINVAL;
}
DebugHelper debug;
if (!debug.ParseCommand(arg)) {
com_debug_help();
global_retc = EINVAL;
return EINVAL;
}
if (debug.execute_locally) {
return global_retc;
}
global_retc = debug.Execute();
return global_retc;
}
//------------------------------------------------------------------------------
// Print help message
//------------------------------------------------------------------------------
void com_debug_help()
{
std::ostringstream oss;
// @todo(faluchet): this needs to be reformatted with 2 spaces as intdentation
// margin in the final display
oss
<< "Usage: debug get|this|<level> [node-queue] [--filter <unitlist>]"
<< std::endl
<< "'[eos] debug ...' allows to get or set the verbosity of the EOS log files in MGM and FST services."
<< std::endl
<< std::endl
<< "Options: "
<< std::endl
<< std::endl
<< "debug get"
<< std::endl
<< "\t : retrieve the current log level for the mgm and fsts node-queue"
<< std::endl
<< std::endl
<< "debug this"
<< std::endl
<< "\t : toggle EOS shell debug mode"
<< std::endl
<< std::endl
<< "debug <level> [--filter <unitlist>]"
<< std::endl
<< "\t : set the MGM where the console is connected to into debug level <level>"
<< std::endl
<< std::endl
<< "debug <level> <node-queue> [--filter <unitlist>]"
<< std::endl
<< "\t : set the <node-queue> into debug level <level>."
<< std::endl
<< "\t - <node-queue> are internal EOS names e.g. '/eos/<hostname>:<port>/fst'"
<< std::endl
<< "\t - <unitlist> is a comma separated list of strings of software units which should be filtered out in the message log!"
<< std::endl
<< "\t The default filter list is: 'Process,AddQuota,Update,UpdateHint,UpdateQuotaStatus,SetConfigValue,Deletion,GetQuota,PrintOut,RegisterNode,SharedHash,listenFsChange,"
<< std::endl
<< "\t placeNewReplicas,placeNewReplicasOneGroup,accessReplicas,accessReplicasOneGroup,accessHeadReplicaMultipleGroup,updateTreeInfo,updateAtomicPenalties,updateFastStructures,work'."
<< std::endl
<< std::endl
<< "The allowed debug levels are: debug info warning notice err crit alert emerg"
<< std::endl
<< std::endl
<< "Examples:" << std::endl
<< " debug info * set MGM & all FSTs into debug mode 'info'"
<< std::endl
<< std::endl
<< " debug err /eos/*/fst set all FSTs into debug mode 'info'"
<< std::endl
<< std::endl
<< " debug crit /eos/*/mgm set MGM into debug mode 'crit'" <<
std::endl
<< std::endl
<< " debug debug --filter MgmOfsMessage set MGM into debug mode 'debug' and filter only messages coming from unit 'MgmOfsMessage'."
<< std::endl
<< std::endl;
std::cerr << oss.str() << std::endl;
}
......@@ -86,6 +86,7 @@ add_library(
proc/admin/Backup.cc
proc/admin/Config.cc
proc/admin/Debug.cc
proc/admin/DebugCmd.cc proc/admin/DebugCmd.hh
proc/admin/FsCmd.cc
proc/admin/Fsck.cc
proc/admin/Fusex.cc
......
......@@ -302,9 +302,8 @@ IProcCommand::ConvertOutputToJsonFormat(std::string stdOut)
while (sline.replace("?configstatus@rw", "_rw")) {}
line = sline.c_str();
std::map <std::string , std::string> map;
std::map <std::string, std::string> map;
eos::common::StringConversion::GetKeyValueMap(line.c_str(), map, "=", " ");
// These values violate the JSON hierarchy and have to be rewritten
eos::common::StringConversion::ReplaceMapKey(map, "cfg.balancer",
"cfg.balancer.status");
......@@ -358,7 +357,6 @@ IProcCommand::ConvertOutputToJsonFormat(std::string stdOut)
// Unquote value
std::stringstream quoted_ss(value);
quoted_ss >> std::quoted(value);
// Seal value
XrdOucString svalue = value.c_str();
XrdMqMessage::Seal(svalue);
......@@ -463,15 +461,16 @@ IProcCommand::HasSlot()
init = true;
for (const auto& type : {
eos::console::RequestProto::kAcl,
eos::console::RequestProto::kNs,
eos::console::RequestProto::kDrain,
eos::console::RequestProto::kFind,
eos::console::RequestProto::kFs,
eos::console::RequestProto::kRm,
eos::console::RequestProto::kStagerRm,
eos::console::RequestProto::kRoute
}) {
eos::console::RequestProto::kAcl,
eos::console::RequestProto::kNs,
eos::console::RequestProto::kDrain,
eos::console::RequestProto::kFind,
eos::console::RequestProto::kFs,
eos::console::RequestProto::kRm,
eos::console::RequestProto::kStagerRm,
eos::console::RequestProto::kRoute,
eos::console::RequestProto::kDebug // @note to check
}) {
mCmdsExecuting.emplace(type, 0ull);
}
}
......
......@@ -31,6 +31,7 @@
#include "mgm/proc/admin/StagerRmCmd.hh"
#include "mgm/proc/admin/IoCmd.hh"
#include "mgm/proc/admin/GroupCmd.hh"
#include "mgm/proc/admin/DebugCmd.hh"
#include <google/protobuf/util/json_util.h>
EOSMGMNAMESPACE_BEGIN
......@@ -216,6 +217,10 @@ ProcInterface::HandleProtobufRequest(const char* path, const char* opaque,
case RequestProto::kGroup:
cmd.reset(new GroupCmd(std::move(req), vid));
case RequestProto::kDebug:
cmd.reset(new DebugCmd(std::move(req), vid));
break;
default:
......@@ -298,6 +303,10 @@ ProcInterface::ProtoIsWriteAccess(const char* path, const char* opaque)
case RequestProto::kGroup:
return false;
case RequestProto::kDebug:
return true; // @note (faluchet) to check
break;
default:
......
//------------------------------------------------------------------------------
// 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 <http://www.gnu.org/licenses/>.*
************************************************************************/
#include "DebugCmd.hh"
#include "mgm/proc/ProcInterface.hh"
#include "mgm/XrdMgmOfs.hh"
#include "mgm/Messaging.hh"
#include "mgm/FsView.hh"
EOSMGMNAMESPACE_BEGIN
//------------------------------------------------------------------------------
// Method implementing the specific behavior of the command executed by the
// asynchronous thread
//------------------------------------------------------------------------------
eos::console::ReplyProto
DebugCmd::ProcessRequest() noexcept
{
eos::console::ReplyProto reply;
eos::console::DebugProto debug = mReqProto.debug();
eos::console::DebugProto::SubcmdCase subcmd = debug.subcmd_case();
switch (subcmd) {
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
//------------------------------------------------------------------------------
int DebugCmd::GetSubcmd(const eos::console::DebugProto_GetProto& get,
eos::console::ReplyProto& reply)
{
eos::common::RWMutexReadLock lock(FsView::gFsView.ViewMutex);
eos::common::Logging& g_logging = eos::common::Logging::GetInstance();
stdOut += "# ------------------------------------------------------------------------------------\n";
stdOut += "# Debug log level\n";
stdOut += "# ....................................................................................\n";
std::string priority = g_logging.GetPriorityString(g_logging.gPriorityLevel);
std::for_each(priority.begin(), priority.end(), [](char& c) {
c = ::tolower(static_cast<unsigned char>(c));
});
stdOut += "/eos/" + (XrdOucString) gOFS->HostName + ':'
+ std::to_string(gOFS->ManagerPort).c_str()
+ "/mgm := \t"
+ priority.c_str()
+ '\n';
auto nodes = FsView::gFsView.mNodeView;
for (auto node = nodes.begin(); node != nodes.end(); ++node) {
stdOut += (node->first + " := \t" +
FsView::gFsView.mNodeView[node->first]->GetConfigMember("debug.state") +
'\n').c_str();
}
reply.set_std_out(stdOut.c_str());
reply.set_std_err(stdErr.c_str());
reply.set_retc(retc);
return SFS_OK;
}
//------------------------------------------------------------------------------
// @todo(faluchet): add a comment on why you need this function and what is
// doing
//------------------------------------------------------------------------------
std::string rebuild_pOpaque(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;
}
//------------------------------------------------------------------------------
// Execute set subcommand
//------------------------------------------------------------------------------
int DebugCmd::SetSubcmd(const eos::console::DebugProto_SetProto& set,
eos::console::ReplyProto& reply)
{
if (mVid.uid != 0) {
stdErr = "error: you have to take role 'root' to execute this command";
retc = EPERM;
} else {
XrdMqMessage message("debug");
// @todo(faluchet): do you still need this commented part?
// @note(faluchet) not really, it is related to the 'rebuild_opaque' fun.
// some function calls of the debug command still relies on the old
// implementation of the command (not-protobuf) and I want to be sure that
// the new behaviour mirrors the old one 1:1.
// I will double check to clean everything before merging.
// int envlen; //
// std::string body = pOpaque->Env(envlen); //
std::string body;
// filter out several *'s ...
int nstars = 0;
int npos = 0;
while ((npos = set.nodename().find("*", npos)) != STR_NPOS) {
npos++;
nstars++;
}
if (nstars > 1) {
stdErr = "error: debug level node can only contain one wildcard character (*) !";
retc = EINVAL;
} else {
body = rebuild_pOpaque(set);
//envlen = body.length();
message.SetBody(body.c_str());
eos::common::Logging& g_logging = eos::common::Logging::GetInstance();
// always check debug level exists first
int debugval = g_logging.GetPriorityByString(set.debuglevel().c_str());
if (debugval < 0) {
stdErr = ("error: debug level " + set.debuglevel() + " is not known!").c_str();
retc = EINVAL;
} else {
if ((set.nodename() == "*") || (set.nodename() == "") ||
(XrdOucString(set.nodename().c_str()) == gOFS->MgmOfsQueue)) {
// this is for us!
// int debugval = g_logging.GetPriorityByString(set.debuglevel().c_str());
g_logging.SetLogPriority(debugval);
stdOut = ("success: debug level is now <" + set.debuglevel() + '>').c_str();
eos_static_notice("setting debug level to <%s>", set.debuglevel());
if (set.filter().length()) {
g_logging.SetFilter(set.filter().c_str());
stdOut += (" filter=" + set.filter()).c_str();
eos_static_notice("setting message logid filter to <%s>", set.filter());
}
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() == "*") {
std::string all_nodes;
all_nodes = "/eos/*/fst";
if (!Messaging::gMessageClient.SendMessage(message, all_nodes.c_str())) {
stdErr = ("error: could not send debug level to nodes mgm.nodename=" +
all_nodes + "\n").c_str();
retc = EINVAL;
} else {
stdOut = ("success: switched to mgm.debuglevel=" + set.debuglevel() +
" on nodes mgm.nodename=" + all_nodes + "\n").c_str();
eos_static_notice("forwarding debug level <%s> to nodes mgm.nodename=%s",
set.debuglevel().c_str(), all_nodes.c_str());
}
all_nodes = "/eos/*/mgm";
// Ignore return value as we've already set the loglevel for the
// current instance. We're doing this only for the slave.
(void) Messaging::gMessageClient.SendMessage(message, all_nodes.c_str());
// if (!Messaging::gMessageClient.SendMessage(message, all_nodes.c_str())) {
// stdErr += ("error: could not send debug level to nodes mgm.nodename=" +
// all_nodes).c_str();
// retc = EINVAL;
// } else {
stdOut += ("success: switched to mgm.debuglevel=" + set.debuglevel() +
" on nodes mgm.nodename=" + all_nodes).c_str();
eos_static_notice("forwarding debug level <%s> to nodes mgm.nodename=%s",
set.debuglevel().c_str(), all_nodes.c_str());
// }
} else {
if (set.nodename() != "") {
// send to the specified list
if (!Messaging::gMessageClient.SendMessage(message, set.nodename().c_str())) {
stdErr = ("error: could not send debug level to nodes mgm.nodename=" +
set.nodename()).c_str();
retc = EINVAL;
} else {
stdOut = ("success: switched to mgm.debuglevel=" + set.debuglevel() +
" on nodes mgm.nodename=" + set.nodename()).c_str();
eos_static_notice("forwarding debug level <%s> to nodes mgm.nodename=%s",
set.debuglevel().c_str(), set.nodename().c_str());
}
}
}
}
}
}
reply.set_std_out(stdOut.c_str());
reply.set_std_err(stdErr.c_str());
reply.set_retc(retc);
return SFS_OK;
}
EOSMGMNAMESPACE_END
//------------------------------------------------------------------------------
// File: DebugCmd.hh
// 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 <http://www.gnu.org/licenses/>.*
************************************************************************/
#pragma once
#include "mgm/Namespace.hh"
#include "proto/Debug.pb.h"
#include "mgm/proc/ProcCommand.hh"
EOSMGMNAMESPACE_BEGIN
//------------------------------------------------------------------------------
//! Class DebugCmd - class handling debug commands
//------------------------------------------------------------------------------
class DebugCmd: public IProcCommand
{
public:
//----------------------------------------------------------------------------
//! Constructor
//!
//! @param req client ProtocolBuffer request
//! @param vid client virtual identity
//----------------------------------------------------------------------------
explicit DebugCmd(eos::console::RequestProto&& req,
eos::common::VirtualIdentity& vid):
IProcCommand(std::move(req), vid, false)
{}
//----------------------------------------------------------------------------
//! Destructor
//----------------------------------------------------------------------------
virtual ~DebugCmd() = default;
//----------------------------------------------------------------------------
//! Method implementing the specific behaviour of the command executed by the
//! asynchronous thread
//----------------------------------------------------------------------------
eos::console::ReplyProto ProcessRequest() noexcept override;
private:
//----------------------------------------------------------------------------
//! Execute get subcommand
//!
//! @param get get subcommand proto object
//! @param reply reply proto object
//----------------------------------------------------------------------------
int GetSubcmd(const eos::console::DebugProto_GetProto& get,
eos::console::ReplyProto& reply);
//----------------------------------------------------------------------------
//! Execute set subcommand
//!
//! @param set set subcommand proto object
//! @param reply reply proto object
//----------------------------------------------------------------------------
int SetSubcmd(const eos::console::DebugProto_SetProto& set,
eos::console::ReplyProto& reply);
};
EOSMGMNAMESPACE_END
Subproject commit a18b6203e8c5c2f10164f3e5aeb8d53041e3d40f
Subproject commit cd695d59961331bb47a36563a43174784320df0c
......@@ -114,18 +114,19 @@ PROTOBUF_GENERATE_CPP(STAGER_RM_SRCS STAGER_RM_HDRS common/cli_proto/StagerRm.pr
PROTOBUF_GENERATE_CPP(ROUTE_SRCS ROUTE_HDRS common/cli_proto/Route.proto)
PROTOBUF_GENERATE_CPP(IO_SRCS IO_HDRS common/cli_proto/Io.proto)
PROTOBUF_GENERATE_CPP(GROUP_SRCS GROUP_HDRS common/cli_proto/Group.proto)
PROTOBUF_GENERATE_CPP(DEBUG_SRCS DEBUG_HDRS common/cli_proto/Debug.proto)
PROTOBUF_GENERATE_CPP(REQ_SRCS REQ_HDRS common/cli_proto/ConsoleRequest.proto)
PROTOBUF_GENERATE_CPP(REP_SRCS REP_HDRS common/cli_proto/ConsoleReply.proto)
set(CLI_PROTO_SRCS
${RECY_SRCS} ${NS_SRCS} ${ACL_SRCS} ${DRAIN_SRCS} ${FIND_SRCS} ${REQ_SRCS}
${REP_SRCS} ${FS_SRCS} ${RM_SRCS} ${STAGER_RM_SRCS} ${ROUTE_SRCS} ${IO_SRCS}
${GROUP_SRCS})
${GROUP_SRCS} ${DEBUG_SRCS})
set(CLI_PROTO_HDRS
${RECY_HDRS} ${NS_HDRS} ${ACL_HDRS} ${DRAIN_HDRS} ${FIND_HDRS} ${REQ_HDRS}
${REP_HDRS} ${FS_HDRS} ${RM_HDRS} ${STAGER_RM_HDRS} ${ROUTE_HDRS} ${IO_HDRS}
${GROUP_HDRS})
${GROUP_HDRS} ${DEBUG_HDRS})
set_source_files_properties(
${CLI_PROTO_SRCS} ${CLI_PROTO_HDRS}
......
......@@ -12,6 +12,7 @@ import "Route.proto";
import "Recycle.proto";
import "Io.proto";
import "Group.proto";
import "Debug.proto";
//------------------------------------------------------------------------------
// Request message sent to the server
......@@ -40,6 +41,7 @@ message RequestProto {
RecycleProto recycle = 10;
IoProto io = 11;
GroupProto group = 12;
DebugProto debug = 13;
}
string Comment = 30;
......