91f4163cafebae3f1d9a3ce6e47207aa0c048605
[senf.git] / senf / Utils / Console / Server.hh
1 // $Id$
2 //
3 // Copyright (C) 2008
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief Server public header */
25
26 #ifndef HH_SENF_Scheduler_Console_Server_
27 #define HH_SENF_Scheduler_Console_Server_ 1
28
29 // Custom includes
30 #include <set>
31 #include <boost/utility.hpp>
32 #include <boost/scoped_ptr.hpp>
33 #include <senf/Scheduler/FdEvent.hh>
34 #include <senf/Scheduler/TimerEvent.hh>
35 #include <senf/Socket/Protocols/INet/INetAddressing.hh>
36 #include <senf/Utils/Logger.hh>
37 #include <senf/Utils/intrusive_refcount.hh>
38 #include "Executor.hh"
39
40 //#include "Server.mpp"
41 #include "Server.ih"
42 //-/////////////////////////////////////////////////////////////////////////////////////////////////
43
44 namespace senf {
45 namespace console {
46
47     class Client;
48
49     /** \brief Interactive console server
50
51         This class provides an interactive console TCP server.
52
53         \idea To support blocking commands, we could give the Client 'suspend()' and 'resume()'
54             members. suspend() would probably throw some kind of exception to transfer control back
55             to the Client instance. on resume(), the command would be called again, maybe setting
56             some flag or something. Example for use: Host name resolution: Here we can just built
57             our own little host-name cache. When the name is not found, we ask the resolver to
58             resolve it and call 'resume' when the name is found. Since it is in the cache now, the
59             command will now complete.
60
61         \ingroup console_access
62       */
63     class Server
64         : public senf::intrusive_refcount
65     {
66         SENF_LOG_CLASS_AREA();
67         SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE );
68     public:
69         //-////////////////////////////////////////////////////////////////////////
70         // Types
71
72         typedef detail::ServerHandle ServerHandle;
73
74         enum Mode { Automatic, Interactive, Noninteractive };
75
76         //-////////////////////////////////////////////////////////////////////////
77
78         static Server & start(senf::INet4SocketAddress const & address);
79                                         ///< Start server on given IPv4 address/port
80         static Server & start(senf::INet6SocketAddress const & address);
81                                         ///< Start server on given IPv6 address/port
82
83         std::string const & name() const; ///< Get server name
84                                         /**< This information is used in the prompt string. */
85
86         Server & name(std::string const & name); ///< Set server name
87                                         /**< This information is used in the prompt string. */
88
89         DirectoryNode & root() const;   ///< Get root node
90
91         Server & root(DirectoryNode & root); ///< Set root node
92                                         /**< \a node will be the root node for all clients launched
93                                              from this server. */
94
95         Mode mode() const;              ///< Get mode
96                                         /**< \see \ref mode(Mode) */
97
98         Server & mode(Mode mode);       ///< Set mode
99                                         /**< There are two Server types:
100                                              \li An interactive server displays a command prompt and
101                                                  optionally supports command-line editing.
102                                              \li A non-interactive server does not display any
103                                                  prompt and does not allow any interactive
104                                                  editing. This type of server is used for (remote)
105                                                  scripting.
106
107                                              The \a mode parameter selects between these modes. In
108                                              \c Automatic (the default), a client connection is
109                                              considered to be interactive if there is no data
110                                              traffic in the first 500ms after the connection is
111                                              opened. */
112
113         void stop();                    ///< Stop the server
114                                         /**< All clients will be closed
115                                              \warning The Server instance itself will be deleted */
116
117     protected:
118
119     private:
120         Server(ServerHandle handle);
121
122         static Server & start(ServerHandle handle);
123
124         void newClient(int event);
125         void removeClient(Client & client);
126
127         ServerHandle handle_;
128         scheduler::FdEvent event_;
129         DirectoryNode::ptr root_;
130         Mode mode_;
131
132         typedef std::set< boost::intrusive_ptr<Client> > Clients;
133         Clients clients_;
134         std::string name_;
135
136         friend class Client;
137     };
138
139     /** \brief Server client instance
140
141         Whenever a new client connects, a new instance of this class is created. This class shows a
142         command prompt, receives the commands, parses them and then passes (using a CommandParser)
143         and passes the commands to an Executor instance.
144
145         \ingroup console_access
146      */
147     class Client
148         : public senf::intrusive_refcount,
149           private boost::base_from_member< detail::NonblockingSocketOStream >,
150           public senf::log::IOStreamTarget
151     {
152         typedef boost::base_from_member< detail::NonblockingSocketOStream > out_t;
153
154         SENF_LOG_CLASS_AREA();
155         SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE );
156
157         static const unsigned INTERACTIVE_TIMEOUT = 500; // milliseconds;
158
159     public:
160         typedef Server::ServerHandle::ClientHandle ClientHandle;
161
162         ~Client();
163
164         void stop();                    ///< Stop the client
165                                         /**< This will close the client socket. */
166
167         std::string const & name() const; ///< Get name of the client instance
168                                         /**< This name is used in the prompt string and is set by
169                                              the server. */
170         ClientHandle handle() const;    ///< Get the client's network socket handle
171         std::ostream & stream();        ///< Get client's output stream
172                                         /**< Data sent to this stream is sent out over the network
173                                              via the client's socket handle. Write operation is
174                                              non-blocking and data may be dropped. Data is written
175                                              using Client::write(). */
176         std::string promptString() const; ///< Get the prompt string
177         DirectoryNode & root() const;   ///< Get configured root node
178         DirectoryNode & cwd() const;    ///< Get current directory
179                                         /**< This is the directory, the console currently is changed
180                                              into by the user of the console. */
181         Server::Mode mode() const;      ///< Get operation mode
182                                         /**< \see Server::mode() */
183         void write(std::string const & data) const;
184                                         ///< Write data to network socket
185                                         /**< The data is automatically filtered depending on the
186                                              type of connection (e.g. on a telnet connection,
187                                              specific bytes are quoted). */
188         std::string const & backtrace() const; ///< Get backtrace of last console error, if any
189         unsigned width() const;         ///< Get console width
190                                         /**< If possible, this will be the width of the connected
191                                              terminal, otherwise a default value (normally 80) is
192                                              returned. */
193
194         static Client & get(std::ostream & os);
195                                         ///< Access client instance
196                                         /**< Allows to access the client instance from console
197                                              command implementations. The first argument to a
198                                              console command is a stream object. \e If this stream
199                                              object belongs to a network console client, this call
200                                              will return the associated Client instance reference.
201                                              \throws std::bad_cast if \a os is not associated with a
202                                                  Client instance. */
203         static unsigned getWidth(std::ostream & os, unsigned defaultWidth = 0,
204                                  unsigned minWidth = 0);
205                                         ///< Get width of client console if possible
206                                         /**< If possible, the width of the client console attached
207                                              to the stream \a os is returned. If this is not
208                                              possible, the \a defaultValue will be used.
209
210                                              If the width obtained this way is smaller than \a
211                                              minWidth, \a defaultValue will be returned instead. */
212
213     protected:
214
215     private:
216         Client(Server & server, ClientHandle handle);
217
218         void setInteractive();
219         void setNoninteractive();
220
221         size_t handleInput(std::string input, bool incremental = false);
222         virtual void v_write(senf::log::time_type timestamp, std::string const & stream,
223                              std::string const & area, unsigned level,
224                              std::string const & message);
225
226         Server & server_;
227         ClientHandle handle_;
228         scheduler::FdEvent readevent_;
229         scheduler::TimerEvent timer_;
230         CommandParser parser_;
231         Executor executor_;
232         std::string name_;
233         boost::scoped_ptr<detail::ClientReader> reader_;
234         Server::Mode mode_;
235         std::string backtrace_;
236
237         friend class Server;
238         friend class detail::ClientReader;
239         friend class detail::NonblockingSocketSink;
240
241         class SysBacktrace
242         {
243             SysBacktrace();
244             static void  backtrace(std::ostream & os);
245             static SysBacktrace instance_;
246         };
247
248     };
249
250     /** \brief Output Console Client instance as it's string representation
251         \related Client
252      */
253     std::ostream & operator<<(std::ostream & os, Client const & client);
254
255     /** \brief Output Console Client instance as it's string representation
256         \related Client
257      */
258     std::ostream & operator<<(std::ostream & os, Client * client);
259
260 }}
261
262 //-/////////////////////////////////////////////////////////////////////////////////////////////////
263 #include "Server.cci"
264 //#include "Server.ct"
265 //#include "Server.cti"
266 #endif
267
268 \f
269 // Local Variables:
270 // mode: c++
271 // fill-column: 100
272 // comment-column: 40
273 // c-file-style: "senf"
274 // indent-tabs-mode: nil
275 // ispell-local-dictionary: "american"
276 // compile-command: "scons -u test"
277 // End: