Utils/Console: Fix singleton instantiation order (ServerManager / Scheduler)
[senf.git] / Console / Server.hh
index dc7630d..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,7 +56,6 @@ namespace console {
 
         This class provides an interactive console TCP server.
 
-        \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
@@ -62,13 +63,11 @@ 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.
-        
-        \implementation We do \e not provide an \c instance() member so we can easily later extend
-            the server to allow registering more than one instance, e.g. with each instance on a
-            differently firewalled port and with different security restrictions.
+
+        \ingroup console_access
       */
     class Server
-        : boost::noncopyable
+        : public senf::intrusive_refcount
     {
         SENF_LOG_CLASS_AREA();
         SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE );
@@ -78,30 +77,64 @@ namespace console {
         
         typedef detail::ServerHandle 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
+
+        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 */
-        
+                                        /**< All clients will be closed 
+                                             \warning The Server instance itself will be deleted */
+
     protected:
 
     private:
         Server(ServerHandle handle);
 
         static Server & start(ServerHandle handle);
-        static boost::scoped_ptr<Server> & instancePtr();
 
-        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_;
@@ -115,6 +148,8 @@ namespace console {
         Whenever a new client connects, a new instance of this class is created. This class shows a
         command prompt, receives the commands, parses them and then passes (using a CommandParser)
         and passes the commands to an Executor instance.
+
+        \ingroup console_access
      */
     class Client
         : public senf::intrusive_refcount, 
@@ -126,6 +161,8 @@ 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;
 
@@ -138,32 +175,50 @@ namespace console {
         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(Server & server, ClientHandle handle, std::string const & name);
+        Client(Server & server, ClientHandle handle);
 
+        void setInteractive();
+        void setNoninteractive();
+        
         void translate(std::string & data);
-        void handleInput(std::string input);
-        virtual void v_write(boost::posix_time::ptime timestamp, std::string const & stream, 
+        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_;
+        SchedulerBinding binding_;
+        SchedulerTimer timer_;
         CommandParser parser_;
         Executor executor_;
         std::string name_;
         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);
 
 }}