X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Console%2FServer.hh;h=1974a748bfe5f7d5c99a8b3a65e363a2e63ac9ed;hb=4e2b741f18b63e97ab4eef30fcee1dec999541be;hp=685eae2a4a8ef9ea2648ec98bc47ba1a277d9e6e;hpb=c70f7413515b513656f850f51a3cc2ea9d776a37;p=senf.git diff --git a/Console/Server.hh b/Console/Server.hh index 685eae2..1974a74 100644 --- a/Console/Server.hh +++ b/Console/Server.hh @@ -31,8 +31,6 @@ #include #include #include -#include -#include #include "../Utils/intrusive_refcount.hh" #include "../Socket/Protocols/INet/TCPSocketHandle.hh" #include "../Socket/ServerSocketHandle.hh" @@ -41,8 +39,10 @@ #include "Parse.hh" #include "Executor.hh" #include "../Socket/Protocols/INet/INetAddressing.hh" +#include "../Utils/Logger.hh" //#include "Server.mpp" +#include "Server.ih" ///////////////////////////////hh.p//////////////////////////////////////// namespace senf { @@ -54,50 +54,89 @@ namespace console { This class provides an interactive console TCP server. - \todo Add readline support - \todo Add interactivity detection using timeout + \idea To support blocking commands, we could give the Client 'suspend()' and 'resume()' + members. suspend() would probably throw some kind of exception to transfer control back + to the Client instance. on resume(), the command would be called again, maybe setting + some flag or something. Example for use: Host name resolution: Here we can just built + our own little host-name cache. When the name is not found, we ask the resolver to + resolve it and call 'resume' when the name is found. Since it is in the cache now, the + command will now complete. + + \ingroup console_access */ class Server - : boost::noncopyable + : public senf::intrusive_refcount { SENF_LOG_CLASS_AREA(); SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE ); public: /////////////////////////////////////////////////////////////////////////// // Types + + typedef detail::ServerHandle ServerHandle; - typedef senf::ServerSocketHandle< - senf::MakeSocketPolicy< senf::TCPv4SocketProtocol::Policy, - senf::UnspecifiedAddressingPolicy>::policy > ServerHandle; + enum Mode { Automatic, Interactive, Noninteractive }; - ~Server(); + /////////////////////////////////////////////////////////////////////////// static Server & start(senf::INet4SocketAddress const & address); ///< Start server on given IPv4 address/port static Server & start(senf::INet6SocketAddress const & address); ///< Start server on given IPv6 address/port - void name(std::string const & name); ///< Set server name + std::string const & name() const; ///< Get server name + /**< This information is used in the prompt string. */ + + Server & name(std::string const & name); ///< Set server name /**< This information is used in the prompt string. */ + DirectoryNode & root() const; ///< Get root node + + Server & root(DirectoryNode & root); ///< Set root node + /**< \a node will be the root node for all clients launched + from this server. */ + + Mode mode() const; ///< Get mode + /**< \see \ref mode(Mode) */ + + Server & mode(Mode mode); ///< Set mode + /**< There are two Server types: + \li An interactive server displays a command prompt and + optionally supports command-line editing. + \li A non-interactive server does not display any + prompt and does not allow any interactive + editing. This type of server is used for (remote) + scripting. + + The \a mode parameter selects between these modes. In + \c Automatic (the default), a client connection is + considered to be interactive if there is no data + traffic in the first 500ms after the connection is + opened. */ + + void stop(); ///< Stop the server + /**< All clients will be closed + \warning The Server instance itself will be deleted */ + protected: private: Server(ServerHandle handle); - static void start(ServerHandle handle); + static Server & start(ServerHandle handle); - void newClient(Scheduler::EventId event); + void newClient(int event); void removeClient(Client & client); ServerHandle handle_; + scheduler::FdEvent event_; + DirectoryNode::ptr root_; + Mode mode_; typedef std::set< boost::intrusive_ptr > Clients; Clients clients_; std::string name_; - static boost::scoped_ptr instance_; - friend class Client; }; @@ -107,45 +146,76 @@ namespace console { command prompt, receives the commands, parses them and then passes (using a CommandParser) and passes the commands to an Executor instance. - \fixme Fix Client::clientData implementation - \fixme Remove the 'dup' needed here so we don't close the same fd twice (see Client - constructor) - \fixme Make output non-blocking (use a non-blocking/discarding streambuf) and possibly set - socket send buffer size - \fixme Don't register a new ReadHelper every round + \ingroup console_access */ class Client - : public senf::intrusive_refcount + : public senf::intrusive_refcount, + private boost::base_from_member< detail::NonblockingSocketOStream >, + public senf::log::IOStreamTarget { + typedef boost::base_from_member< detail::NonblockingSocketOStream > out_t; + SENF_LOG_CLASS_AREA(); SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE ); + + static const unsigned INTERACTIVE_TIMEOUT = 500; // milliseconds; + public: typedef Server::ServerHandle::ClientSocketHandle ClientHandle; ~Client(); - void stopClient(); ///< Stop the client + void stop(); ///< Stop the client /**< This will close the client socket. */ + std::string const & name() const; + ClientHandle handle() const; + std::ostream & stream(); + std::string promptString() const; + DirectoryNode & root() const; + Server::Mode mode() const; + + static Client & get(std::ostream & os); + protected: private: - Client(ClientHandle handle, std::string const & name); + Client(Server & server, ClientHandle handle); - void clientData(ReadHelper::ptr helper); - void showPrompt(); + void setInteractive(); + void setNoninteractive(); + + void translate(std::string & data); + unsigned handleInput(std::string input, bool incremental = false); + virtual void v_write(senf::log::time_type timestamp, std::string const & stream, + std::string const & area, unsigned level, + std::string const & message); + Server & server_; ClientHandle handle_; - std::string tail_; + scheduler::FdEvent readevent_; + scheduler::TimerEvent timer_; CommandParser parser_; Executor executor_; std::string name_; - - typedef boost::iostreams::stream fdostream; - fdostream out_; + std::string lastCommand_; + boost::scoped_ptr reader_; + Server::Mode mode_; friend class Server; + friend class detail::ClientReader; + friend class detail::NonblockingSocketSink; }; + + /** \brief Output Console Client instance as it's string representation + \related Client + */ + std::ostream & operator<<(std::ostream & os, Client const & client); + + /** \brief Output Console Client instance as it's string representation + \related Client + */ + std::ostream & operator<<(std::ostream & os, Client * client); }}