aef6bc542605caf427beb5ce9d6c4fb744a042e0
[senf.git] / senf / Utils / Console / Server.cc
1 // $Id$
2 //
3 // Copyright (C) 2008
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 //
6 // The contents of this file are subject to the Fraunhofer FOKUS Public License
7 // Version 1.0 (the "License"); you may not use this file except in compliance
8 // with the License. You may obtain a copy of the License at 
9 // http://senf.berlios.de/license.html
10 //
11 // The Fraunhofer FOKUS Public License Version 1.0 is based on, 
12 // but modifies the Mozilla Public License Version 1.1.
13 // See the full license text for the amendments.
14 //
15 // Software distributed under the License is distributed on an "AS IS" basis, 
16 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
17 // for the specific language governing rights and limitations under the License.
18 //
19 // The Original Code is Fraunhofer FOKUS code.
20 //
21 // The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V. 
22 // (registered association), Hansastraße 27 c, 80686 Munich, Germany.
23 // All Rights Reserved.
24 //
25 // Contributor(s):
26 //   Stefan Bund <g0dil@berlios.de>
27
28 /** \file
29     \brief Server non-inline non-template implementation */
30
31 #include "Server.hh"
32 //#include "Server.ih"
33
34 // Custom includes
35 #include <boost/algorithm/string/trim.hpp>
36 #include <boost/bind.hpp>
37 #include <senf/Utils/membind.hh>
38 #include <senf/Utils/Logger/SenfLog.hh>
39 #include <senf/Version.hh>
40 #include "LineEditor.hh"
41 #include "ScopedDirectory.hh"
42 #include "Sysdir.hh"
43 #include "SysInfo.hh"
44 #include "ParsedCommand.hh"
45
46 //#include "Server.mpp"
47 #define prefix_
48 //-/////////////////////////////////////////////////////////////////////////////////////////////////
49
50 #ifdef SENF_DEBUG
51 #   define BUILD_TYPE "development"
52 #else
53 #   define BUILD_TYPE "production"
54 #endif
55
56 namespace {
57     senf::console::SysInfo::Proxy addSysInfo (
58             "SENF: The Simple and Extensible Network Framework\n"
59             "  © 2006-2011 Fraunhofer Institute for Open Communication Systems, Network Research\n"
60             "  Contact: senf-dev@lists.berlios.de\n"
61             "  Version: " SENF_LIB_VERSION " Revision number: " SENF_REVISION "\n"
62             "  Build-type: " BUILD_TYPE ", SenfLog compile time limit: " +
63             senf::str(senf::log::LEVELNAMES[senf::SenfLog::compileLimit::value]), 0);
64 }
65
66 //-/////////////////////////////////////////////////////////////////////////////////////////////////
67 // senf::console::detail::SocketStreamSink
68
69 prefix_ std::streamsize senf::console::detail::SocketStreamSink::write(const char * s,
70                                                                             std::streamsize n)
71 {
72 // since handle is now non blocking we done check for writeable
73     try {
74 //        if (client_.handle().writeable()) {
75             std::string data (s, n);
76             client_.write(data);
77 //        }
78     }
79     catch (...) {}
80     return n;
81 }
82
83 //-/////////////////////////////////////////////////////////////////////////////////////////////////
84 // senf::console::Server
85
86 prefix_ senf::console::Server &
87 senf::console::Server::start(senf::INet4SocketAddress const & address)
88 {
89     senf::TCPv4ServerSocketHandle handle (address);
90     Server & server (senf::console::Server::start(handle));
91     SENF_LOG((Server::SENFLogArea)(log::NOTICE)(
92                  "Console server started at " << address ));
93     return server;
94 }
95
96 prefix_ senf::console::Server &
97 senf::console::Server::start(senf::INet6SocketAddress const & address)
98 {
99     senf::TCPv6ServerSocketHandle handle (address);
100     Server & server (senf::console::Server::start(handle));
101     SENF_LOG((Server::SENFLogArea)(log::NOTICE)(
102                  "Console server started at " << address ));
103     return server;
104 }
105
106 prefix_ senf::console::Server & senf::console::Server::start(ServerHandle handle)
107 {
108     boost::intrusive_ptr<Server> p (new Server(handle));
109     detail::ServerManager::add(boost::intrusive_ptr<Server>(p));
110     return *p;
111 }
112
113 prefix_ senf::console::Server::Server(ServerHandle handle)
114     : handle_ (handle),
115       event_ ("senf::console::Server", senf::membind(&Server::newClient, this),
116               handle_, scheduler::FdEvent::EV_READ),
117       root_ (senf::console::root().thisptr()), mode_ (Automatic),
118       name_ (::program_invocation_short_name)
119 {}
120
121 prefix_ void senf::console::Server::newClient(int event)
122 {
123     ServerHandle::ClientHandle client (handle_.accept());
124     boost::intrusive_ptr<Client> p (new Client(*this, client));
125     clients_.insert( p );
126     SENF_LOG(( "Registered new client " << client.peer() ));
127 }
128
129 prefix_ void senf::console::Server::removeClient(Client & client)
130 {
131     SENF_LOG_BLOCK(({
132                 log << "Disposing client ";
133                 try {
134                     log << client.handle().peer();
135                 }
136                 catch (senf::SystemException & ex) {
137                     log << "(dead socket)";
138                 }
139             }));
140     // THIS DELETES THE CLIENT INSTANCE !!
141     clients_.erase(boost::intrusive_ptr<Client>(&client));
142 }
143
144 //-/////////////////////////////////////////////////////////////////////////////////////////////////
145 // senf::console::detail::DumbClientReader
146
147 prefix_ senf::console::detail::DumbClientReader::DumbClientReader(Client & client)
148     : ClientReader(client), promptLen_ (0), promptActive_ (false)
149 {
150     showPrompt();
151     ReadHelper<ClientHandle>::dispatch( handle(), 16384u, ReadUntil("\n"),
152                                         senf::membind(&DumbClientReader::clientData, this) );
153 }
154
155 prefix_ void
156 senf::console::detail::DumbClientReader::clientData(senf::ReadHelper<ClientHandle>::ptr helper)
157 {
158     if (helper->error() || handle().eof()) {
159         // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER stopClient RETURNS
160         stopClient();
161         return;
162     }
163
164     promptLen_ = 0;
165     promptActive_ = false;
166
167     std::string data (tail_ + helper->data());
168     tail_ = helper->tail();
169     boost::trim(data); // Gets rid of superfluous  \r or \n characters
170     handleInput(data);
171
172     showPrompt();
173     ReadHelper<ClientHandle>::dispatch( handle(), 16384u, ReadUntil("\n"),
174                                         senf::membind(&DumbClientReader::clientData, this) );
175
176 }
177
178 prefix_ void senf::console::detail::DumbClientReader::showPrompt()
179 {
180     std::string prompt (promptString());
181     prompt += " ";
182
183     stream() << std::flush;
184     promptLen_ = prompt.size();
185     promptActive_ = true;
186     v_write(prompt);
187 }
188
189 prefix_ void senf::console::detail::DumbClientReader::v_disablePrompt()
190 {
191     if (promptActive_ && promptLen_ > 0) {
192         stream() << '\r' << std::string(' ', promptLen_) << '\r';
193         promptLen_ = 0;
194     }
195 }
196
197 prefix_ void senf::console::detail::DumbClientReader::v_enablePrompt()
198 {
199     if (promptActive_ && ! promptLen_)
200         showPrompt();
201 }
202
203 prefix_ void senf::console::detail::DumbClientReader::v_write(std::string const & data)
204 {
205     try {
206         handle().write(data);
207     }
208     catch (senf::ExceptionMixin & ex) {
209         SENF_LOG(("unexpected failure writing to socket:" << ex.message()));
210         try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
211         catch (...) {}
212     }
213     catch (std::exception & ex) {
214         SENF_LOG(("unexpected failure writing to socket:" << ex.what()));
215         try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
216         catch (...) {}
217     }
218     catch (...) {
219         SENF_LOG(("unexpected failure writing to socket: unknown exception"));
220         try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
221         catch (...) {}
222     }
223 }
224
225 prefix_ unsigned senf::console::detail::DumbClientReader::v_width()
226     const
227 {
228     return 80;
229 }
230
231 //-/////////////////////////////////////////////////////////////////////////////////////////////////
232 // senf::console::detail::NoninteractiveClientReader
233
234 prefix_
235 senf::console::detail::NoninteractiveClientReader::NoninteractiveClientReader(Client & client)
236     : ClientReader (client), streamBufferMaxSize_( 1024*1024),
237       readevent_ ("senf::console::detail::NoninteractiveClientReader",
238                   senf::membind(&NoninteractiveClientReader::newData, this),
239                   handle(), senf::scheduler::FdEvent::EV_READ),
240       writeevent_ ("senf::console::detail::NoninteractiveClientReader",
241                   membind(&NoninteractiveClientReader::writeHandler, this), handle(),
242                   scheduler::FdEvent::EV_WRITE, false)
243 {}
244
245 prefix_ void senf::console::detail::NoninteractiveClientReader::v_disablePrompt()
246 {}
247
248 prefix_ void senf::console::detail::NoninteractiveClientReader::v_enablePrompt()
249 {}
250
251 prefix_ void senf::console::detail::NoninteractiveClientReader::streamBufferMaxSize(SendQueue::size_type size)
252 {
253     streamBufferMaxSize_ = size;
254 }
255
256 prefix_ senf::console::detail::NoninteractiveClientReader::SendQueue::size_type
257 senf::console::detail::NoninteractiveClientReader::streamBufferMaxSize()
258     const
259 {
260     return streamBufferMaxSize_;
261 }
262
263 prefix_ void senf::console::detail::NoninteractiveClientReader::v_write(std::string const & data)
264 {
265     if( sendQueue_.size() > streamBufferMaxSize_)
266         return;
267     sendQueue_.insert( sendQueue_.end(), data.begin(), data.end());
268     writeHandler(scheduler::FdEvent::EV_WRITE);
269     if (! sendQueue_.empty())
270         writeevent_.enable();
271
272 }
273
274 prefix_ unsigned senf::console::detail::NoninteractiveClientReader::v_width()
275     const
276 {
277     return 80;
278 }
279
280 prefix_ void
281 senf::console::detail::NoninteractiveClientReader::newData(int event)
282 {
283     if (event != senf::scheduler::FdEvent::EV_READ || handle().eof()) {
284         if (! buffer_.empty())
285             handleInput(buffer_);
286         stopClient();
287         return;
288     }
289
290     std::string::size_type n (buffer_.size());
291     buffer_.resize(n + handle().available());
292     buffer_.erase(handle().read(boost::make_iterator_range(buffer_.begin()+n, buffer_.end())),
293                   buffer_.end());
294     buffer_.erase(0, handleInput(buffer_, true));
295     stream() << std::flush;
296 }
297
298 prefix_ void
299 senf::console::detail::NoninteractiveClientReader::writeHandler(int event)
300 {
301     if (event != senf::scheduler::FdEvent::EV_WRITE) {
302         writeevent_.disable();
303         readevent_.disable();
304         try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
305         catch (...) {}
306         return;
307     }
308     try {
309         sendQueue_.erase(sendQueue_.begin(),
310                 handle().write(boost::make_iterator_range(sendQueue_.begin(), sendQueue_.end())));
311     }
312     catch (senf::ExceptionMixin & ex) {
313         SENF_LOG(("unexpected failure writing to socket:" << ex.message()));
314         try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
315         catch (...) {}
316     }
317     catch (std::exception & ex) {
318         SENF_LOG(("unexpected failure writing to socket:" << ex.what()));
319         try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
320         catch (...) {}
321     }
322     catch (...) {
323         SENF_LOG(("unexpected failure writing to socket: unknown exception"));
324         try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
325         catch (...) {}
326     }
327     if (sendQueue_.empty())
328         writeevent_.disable();
329
330 }
331
332 //-/////////////////////////////////////////////////////////////////////////////////////////////////
333 // senf::console::Client
334
335 prefix_ senf::console::Client::Client(Server & server, ClientHandle handle)
336     : out_t(boost::ref(*this)),
337       senf::log::IOStreamTarget("client-" + senf::str(handle.peer()), out_t::member),
338       server_ (server), handle_ (handle),
339       readevent_ ("senf::console::Client::interactive_check",
340                   boost::bind(&Client::setNoninteractive,this),
341                   handle, scheduler::FdEvent::EV_READ, false),
342       timer_ ("senf::console::Client::interactive_timer",
343               boost::bind(&Client::setInteractive, this),
344               scheduler::eventTime() + ClockService::milliseconds(INTERACTIVE_TIMEOUT),
345               false),
346       name_ (server.name()), reader_ (), mode_ (server.mode())
347 {
348     handle_.facet<senf::TCPSocketProtocol>().nodelay(true);
349     handle_.blocking(false);
350     executor_.chroot(root());
351     switch (mode_) {
352     case Server::Interactive :
353         setInteractive();
354         break;
355     case Server::Noninteractive :
356         setNoninteractive();
357         break;
358     case Server::Automatic :
359         readevent_.enable();
360         timer_.enable();
361         break;
362     }
363 }
364
365 prefix_ void senf::console::Client::setInteractive()
366 {
367     readevent_.disable();
368     timer_.disable();
369     mode_ = Server::Interactive;
370     reader_.reset(new detail::LineEditorSwitcher (*this));
371     executor_.autocd(true).autocomplete(true);
372 }
373
374 prefix_ void senf::console::Client::setNoninteractive()
375 {
376     readevent_.disable();
377     timer_.disable();
378     mode_ = Server::Noninteractive;
379     detail::NoninteractiveClientReader * newReader (new detail::NoninteractiveClientReader(*this));
380     reader_.reset( newReader);
381     consoleDir().add( "streamBuffer", senf::console::factory::Command( senf::membind(
382             SENF_MEMFNP( detail::NoninteractiveClientReader::SendQueue::size_type, detail::NoninteractiveClientReader, streamBufferMaxSize, () const ),
383             newReader )));
384     consoleDir().add( "streamBuffer", senf::console::factory::Command( senf::membind(
385             SENF_MEMFNP( void, detail::NoninteractiveClientReader, streamBufferMaxSize, (detail::NoninteractiveClientReader::SendQueue::size_type) ),
386             newReader )));
387 }
388
389 prefix_ std::string::size_type senf::console::Client::handleInput(std::string data,
390                                                                   bool incremental)
391 {
392     std::string::size_type n (data.size());
393
394     try {
395         if (incremental)
396             n = parser_.parseIncremental(data, boost::bind<void>( boost::ref(executor_),
397                                                                   boost::ref(stream()),
398                                                                   _1 ));
399         else
400             parser_.parse(data, boost::bind<void>( boost::ref(executor_),
401                                                    boost::ref(stream()),
402                                                    _1 ));
403     }
404     catch (Executor::ExitException &) {
405         // This generates an EOF condition on the Handle. This EOF condition is expected to be
406         // handled gracefully by the ClientReader. We cannot call stop() here, since we are called
407         // from the client reader callback and that will continue executing after stop() has been
408         // called. stop() however will delete *this instance ... BANG ...
409         try { handle_.facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
410         catch (...) {}
411     }
412     catch (std::exception & ex) {
413         std::string msg (ex.what());
414         std::string::size_type i (msg.find("-- \n"));
415         if (i != std::string::npos) {
416             backtrace_ = msg.substr(0,i);
417             msg = msg.substr(i+4);
418         } else
419
420         stream() << msg << std::endl;
421     }
422     catch (...) {
423         stream() << "unidentified error (unknown exception thrown)" << std::endl;
424     }
425     return n;
426 }
427
428 prefix_ void senf::console::Client::v_write(senf::log::time_type timestamp,
429                                             std::string const & stream,
430                                             std::string const & area, unsigned level,
431                                             std::string const & message)
432 {
433     reader_->disablePrompt();
434     IOStreamTarget::v_write(timestamp, stream, area, level, message);
435     out_t::member << std::flush;
436     reader_->enablePrompt();
437 }
438
439 prefix_ unsigned senf::console::Client::getWidth(std::ostream & os, unsigned defaultWidth,
440                                                  unsigned minWidth)
441 {
442     unsigned rv (defaultWidth);
443     try {
444         rv = get(os).width();
445     }
446     catch (std::bad_cast &) {}
447     return rv < minWidth ? defaultWidth : rv;
448 }
449
450 //-/////////////////////////////////////////////////////////////////////////////////////////////////
451 // senf::console::Client::SysBacktrace
452 prefix_ senf::console::Client::SysBacktrace::SysBacktrace()
453 {
454     sysdir().add("backtrace", factory::Command(&SysBacktrace::backtrace)
455                  .doc("Display the backtrace of the last error / exception in this console") );
456 }
457
458 prefix_ void senf::console::Client::SysBacktrace::backtrace(std::ostream & os)
459 {
460     Client & client (Client::get(os));
461     if (client.backtrace().empty())
462         os << "(no backtrace)\n";
463     else
464         os << client.backtrace();
465 }
466
467 senf::console::Client::SysBacktrace senf::console::Client::SysBacktrace::instance_;
468
469 //-/////////////////////////////////////////////////////////////////////////////////////////////////
470 #undef prefix_
471 //#include "Server.mpp"
472
473 \f
474 // Local Variables:
475 // mode: c++
476 // fill-column: 100
477 // comment-column: 40
478 // c-file-style: "senf"
479 // indent-tabs-mode: nil
480 // ispell-local-dictionary: "american"
481 // compile-command: "scons -u test"
482 // End: