Utils/Console: Fix singleton instantiation order (ServerManager / Scheduler)
[senf.git] / Console / Server.hh
index 3b7a93e..b7684d5 100644 (file)
@@ -35,6 +35,8 @@
 #include "../Socket/Protocols/INet/TCPSocketHandle.hh"
 #include "../Socket/ServerSocketHandle.hh"
 #include "../Scheduler/Scheduler.hh"
+#include "../Scheduler/Binding.hh"
+#include "../Scheduler/Timer.hh"
 #include "../Scheduler/ReadHelper.hh"
 #include "Parse.hh"
 #include "Executor.hh"
@@ -54,8 +56,6 @@ 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
@@ -63,19 +63,23 @@ namespace console {
             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();
 
@@ -83,23 +87,54 @@ namespace console {
                                         ///< Start server on given IPv4 address/port
         static Server & start(senf::INet6SocketAddress const & address);
                                         ///< Start server on given IPv6 address/port
-        static Server & instance();
 
-        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 boost::scoped_ptr<Server> & instancePtr();
+        static Server & start(ServerHandle handle);
 
-        void newClient(Scheduler::EventId event);
+        void newClient(int event);
         void removeClient(Client & client);
         
         ServerHandle handle_;
+        DirectoryNode::ptr root_;
+        Mode mode_;
         
         typedef std::set< boost::intrusive_ptr<Client> > Clients;
         Clients clients_;
@@ -114,8 +149,7 @@ 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 Don't register a new ReadHelper every round
+        \ingroup console_access
      */
     class Client
         : public senf::intrusive_refcount, 
@@ -126,36 +160,65 @@ namespace console {
 
         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);
-
-        void clientData(ReadHelper<ClientHandle>::ptr helper);
-        void showPrompt();
+        Client(Server & server, ClientHandle handle);
 
-        virtual void v_write(boost::posix_time::ptime timestamp, std::string const & stream, 
+        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_;
+        SchedulerBinding binding_;
+        SchedulerTimer timer_;
         CommandParser parser_;
         Executor executor_;
         std::string name_;
-        unsigned promptLen_;
         std::string lastCommand_;
+        boost::scoped_ptr<detail::ClientReader> 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);
 
 }}