//------------------------------------------------------------------------------
//! @file ProcInterface.hh
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2017 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 .*
************************************************************************/
#pragma once
#include "mgm/Namespace.hh"
#include "mgm/proc/IProcCommand.hh"
#include "common/Logging.hh"
#include "common/Mapping.hh"
#include "common/ThreadPool.hh"
#include "proc_fs.hh"
#include
//! Forward declarations
class XrdSecEntity;
EOSMGMNAMESPACE_BEGIN
//------------------------------------------------------------------------------
//! @file ProcInterface.hh
//!
//! @brief ProcCommand class handling proc commands
//!
//! A proc command is identified by a user requesting to read a path like
//! '/proc/user' or '/proc/admin'. These two options specify either user or
//! admin commands. Admin commands can only be executed if a VID indicates
//! membership in the admin group, root or in some cases 'sss' authenticated
//! clients. A proc command is usually referenced with the tag 'mgm.cmd'.
//! In some cases there are sub commands defined by 'mgm.subcmd'.
//! Proc commands are executed in the 'open' function and the results
//! are provided as stdOut,stdErr and a return code which is assembled in an
//! opaque output stream with 3 keys indicating the three return objects.
//! The resultstream is streamed by a client like a file read using 'xrdcp'
//! issuing several read requests. On close the resultstream is freed.
//!
//! The implementations of user commands are found under mgm/proc/user/X.cc
//! The implementations of admin commands are found under mgm/proc/admin/X.cc
//! A new command has to be added to the if-else construct in the open function.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//! Class ProcInterface
//------------------------------------------------------------------------------
class ProcInterface: public eos::common::LogId
{
public:
//----------------------------------------------------------------------------
//! Factory method to get ProcCommand object
//!
//! @param tident client connection unique identifier
//! @param vid virtual id of the client
//! @param path input path for proc command
//! @param opaque input opaque information
//! @param log_id log id passed from the calling method
//!
//! @return ProcCommand object
//----------------------------------------------------------------------------
static std::unique_ptr
GetProcCommand(const char* tident, eos::common::VirtualIdentity& vid,
const char* path = 0, const char* opaque = 0,
const char* log_id = 0);
//----------------------------------------------------------------------------
//! Check if a path is requesting a proc command
//!
//! @param path input path for a proc command
//!
//! @return true if proc command otherwise false
//----------------------------------------------------------------------------
static bool IsProcAccess(const char* path);
//----------------------------------------------------------------------------
//! Check if a proto proc command contains a 'write' action on the instance
//----------------------------------------------------------------------------
static bool ProtoIsWriteAccess(const char* opaque);
//----------------------------------------------------------------------------
//! Check if a proc command contains a 'write' action on the instance
//!
//! @param path input arguments for proc command
//! @param info CGI for proc command
//!
//! @return true if write access otherwise false
//----------------------------------------------------------------------------
static bool IsWriteAccess(const char* path, const char* info);
//----------------------------------------------------------------------------
//! Authorize if the virtual ID can execute the requested command
//!
//! @param path specifies user or admin command path
//! @param info CGI providing proc arguments
//! @param vid virtual id of the client
//! @param entity security entity object
//!
//! @return true if authorized otherwise false
//----------------------------------------------------------------------------
static bool Authorize(const char* path, const char* info,
eos::common::VirtualIdentity& vid,
const XrdSecEntity* entity);
//----------------------------------------------------------------------------
//! Get asynchronous executing command, submitted earlier by the same client
//! who cames to pick up the result.
//!
//! @param tident unique client connection identifier
//!
//! @return proc client command object or nullptr
//----------------------------------------------------------------------------
static std::unique_ptr GetSubmittedCmd(const char* tident);
//----------------------------------------------------------------------------
//! Save asynchronous executing command, so we can stall the client and
//! return later on the result.
//!
//! @param tident unique client connection identifier
//! @param pcmd proc command object
//!
//! @return true if command saved, otherwise false
//----------------------------------------------------------------------------
static bool SaveSubmittedCmd(const char* tident,
std::unique_ptr&& pcmd);
//----------------------------------------------------------------------------
//! Drop asynchronously executing command since the client disconnected
//!
//! @param tident unique client connection identifier
//----------------------------------------------------------------------------
static void DropSubmittedCmd(const char* tident);
///! Pool of threads executing asynchronously long-running client commands
static eos::common::ThreadPool sProcThreads;
private:
//----------------------------------------------------------------------------
//! Handle protobuf request
//!
//! @parm path input path of a proc command
//! @param opaque full opaque info containing the base64 protocol request
//! @param vid virtual identity of the client
//!
//! @return unique pointer to ProcCommand object or null otherwise
//----------------------------------------------------------------------------
static unique_ptr
HandleProtobufRequest(const char* opaque, eos::common::VirtualIdentity& vid);
//----------------------------------------------------------------------------
//! Handle protobuf request
//!
//! @parm protobuf request object
//! @param vid virtual identity of the client
//!
//! @return unique pointer to ProcCommand object or null otherwise
//----------------------------------------------------------------------------
static std::unique_ptr
HandleProtobufRequest(eos::console::RequestProto& req,
eos::common::VirtualIdentity& vid);
//! Map of command id to async proc commands
static std::unordered_map> mMapCmds;
//! List of running command without an associated client waiting for their
//! response
static std::list> mCmdToDel;
static std::mutex mMutexCmds; ///< Mutex protecting access to the map above
static thread_local eos::common::LogId tlLogId; ///< Thread local log id
};
EOSMGMNAMESPACE_END