Update of /cvsroot/simspark/simspark/spark/zeitgeist/telnetserver In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11684/telnetserver Added Files: telnetdaemon.cpp telnetdaemon.h telnetserver.cpp telnetserver.h telnetserver_c.cpp telnetsession.cpp telnetsession.h Log Message: --- NEW FILE: telnetsession.cpp --- #include "telnetsession.h" #include "telnetdaemon.h" #include "telnetserver.h" #include "../scriptserver/scriptserver.h" #include <iostream> using namespace zeitgeist; using namespace boost; TelnetSession::TelnetSession(TelnetDaemon &daemon) : mClientSocket(INVALID_SOCKET), mDaemon(daemon) { memset(&mClientAddr, 0, sizeof(mClientAddr)); // options mDoEcho = false; } TelnetSession::~TelnetSession() { } void TelnetSession::Init(SOCKET clientSocket, sockaddr_in clientAddr, boost::shared_ptr<ScriptServer> scriptServer) { mClientSocket = clientSocket; mClientAddr = clientAddr; mScriptServer = scriptServer; } void TelnetSession::operator()() { // attach ourselves to the daemon which created us mDaemon.Attach(this); bool done = false; while(!done) { Send(mDaemon.GetServer().GetHostName()+": "); std::string input; if (WaitForData(input) == false) { done = true; } else { if (input.compare("exit")==0) { Terminate(); } else { mScriptServer->Eval(input); //Send("Executing: '"+input+"'\r\n"); } } } // remove ourselves from the daemon mDaemon.Detach(this); closesocket(mClientSocket); } bool TelnetSession::Send(const std::string& data) { if ( send( mClientSocket , data.c_str() , data.size() , 0 ) != (int)data.size() ) { std::cout << "ERROR: Sending data to client failed" << std::endl; return false; } return true; } bool TelnetSession::WaitForData(std::string &data) { const unsigned char IAC = 255; // interpret as command // global would be faster.... std::string validChars = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()-_=+{[}]\\|;:'\"<,>.?/"; int sizeEchoBuf = 0; char echoBuf[4]; // empty the buffer memset( echoBuf , 0 , 4 ); unsigned char buf = 0; while ( buf != 13 ) { buf = 0; memset( echoBuf , 0 , 4 ); sizeEchoBuf = 0; if(recv( mClientSocket, (char*)&buf , 1 , 0) < 1) { //std::cout << "ERROR: Receiving data from client failed" << std::endl; return false; } switch((unsigned char)buf) { case 13: // return was pressed echoBuf[0] = 13; echoBuf[1] = 10; sizeEchoBuf = 2; break; case 8: // backspace was pressed case 127: // backspace was pressed if ( data.size() > 0 ) { // cut off one char data.resize(data.size()-1); echoBuf[0] = buf; echoBuf[1] = 32; echoBuf[2] = buf; sizeEchoBuf = 3; } else { continue; } break; case IAC: { recv( mClientSocket, (char*)&buf, 1 , 0); ProcessCommand(buf); } break; default: if (validChars.find(buf) != -1) { // add to result buffer data += buf; // echo the pressed char echoBuf[0] = buf; sizeEchoBuf = 1; } break; } // echo if ( mDoEcho == true && sizeEchoBuf>0) { if (send( mClientSocket , echoBuf , sizeEchoBuf , 0 ) != sizeEchoBuf) { std::cout << "ERROR: Sending data to client failed" << std::endl; return false; } } } return true; } void TelnetSession::Terminate() { shutdown(mClientSocket, 0x01); } void TelnetSession::ProcessCommand(unsigned char command) { unsigned char option = 0; std::cout << "Command: " << (unsigned int)command; if (command >= 251 && command <= 254) { // these commands need another byte from the client unsigned char buf; recv( mClientSocket, (char*)&buf, 1 , 0); option = buf; std::cout << " - " << (unsigned int)option; } std::cout << std::endl; switch (command) { case 253: // DO switch(option) { case 1: // do echo mDoEcho = true; break; } break; case 254: // DON'T switch(option) { case 1: // do echo mDoEcho = false; break; } break; }; } --- NEW FILE: telnetdaemon.cpp --- #include "telnetdaemon.h" #include "telnetserver.h" #include "telnetsession.h" #include <boost/thread/thread.hpp> #include <iostream> using namespace zeitgeist; using namespace std; using namespace boost; TelnetDaemon::TelnetDaemon(TelnetServer &server) : mServer(server) { mDaemonSocket = INVALID_SOCKET; } TelnetDaemon::~TelnetDaemon() { Terminate(); } /* The actual thread routine. */ void TelnetDaemon::operator()() { //tell the server that the daemon thread is running mServer.SetDaemon(this); // at this point the bidirectional connection between client/server is // established. Now we start the listening socket of the daemon if (Init(mServer.GetPort()) == false) return; std::cout << mServer.GetHostName() << ": Daemon started, listening for connections..." << std::endl; boost::thread_group sessionThreads; TelnetSession cc(*this); while (AcceptConnection(cc)) { sessionThreads.create_thread(cc); } // we wait for all session threads to finish, before we finish sessionThreads.join_all(); } bool TelnetDaemon::Init(int portNr) { // create socket mDaemonSocket = socket(AF_INET, SOCK_STREAM, 0); if (mDaemonSocket == INVALID_SOCKET) { std::cout << "ERROR: Could not create socket" << std::endl; return false; } int iOptval = 1; if ( setsockopt( mDaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *) &iOptval, sizeof(int)) ) { std::cout << "ERROR: Setsockopt failed" << std::endl; return false; } mDaemonAddr.sin_family = AF_INET; mDaemonAddr.sin_port = htons( USHORT(portNr) ); mDaemonAddr.sin_addr.s_addr = INADDR_ANY; // Bind to the given port if ( bind( mDaemonSocket, (struct sockaddr *) &mDaemonAddr, sizeof(mDaemonAddr) ) ) { std::cout << "ERROR: Bind failed" << std::endl; return false; } // change to passive socket if ( listen( mDaemonSocket , SOMAXCONN ) ) { std::cout << "ERROR: Listen failed" << std::endl; return false; } return true; } void TelnetDaemon::Terminate() { if (mDaemonSocket != INVALID_SOCKET) { closesocket(mDaemonSocket); mDaemonSocket = INVALID_SOCKET; } } bool TelnetDaemon::AcceptConnection(TelnetSession &cc) { sockaddr_in clientAddr; SOCKET clientSocket; // fill with zero terms memset( &clientAddr , 0 , sizeof(clientAddr) ); // wait for connetion int len = sizeof(clientAddr); clientSocket = accept( mDaemonSocket, (struct sockaddr *)&(clientAddr) , &len ); if ( clientSocket == INVALID_SOCKET ) { //std::cout << "ERROR: Accept failed" << std::endl; return false; } // get client ip address char szClientIp[255]; strncpy( szClientIp, inet_ntoa(clientAddr.sin_addr), 128); std::cout << "Incoming connection from: " << szClientIp << std::endl; // create a clientconnection cc.Init(clientSocket, clientAddr, mServer.GetScriptServer()); return true; } void TelnetDaemon::Attach(TelnetSession *session) { std::cout << "Attaching TelnetSession " << (void*) session << std::endl; mSessions.push_back(session); } void TelnetDaemon::Detach(TelnetSession *session) { std::cout << "Detaching TelnetSession " << (void*) session << std::endl; mSessions.remove(session); } void TelnetDaemon::Status() { std::cout << " Number of clients: " << mSessions.size() << std::endl; } --- NEW FILE: telnetdaemon.h --- /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- this file is part of rcssserver3D Fri May 9 2003 Copyright (C) 2002,2003 Koblenz University Copyright (C) 2003 RoboCup Soccer Server 3D Maintenance Group $Id: telnetdaemon.h,v 1.1 2005/12/05 21:05:01 rollmark Exp $ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. TelnetDaemon HISTORY: 19.06.2002 MK - initial version */ #ifndef ZEITGEIST_TELNETDAEMON_H #define ZEITGEIST_TELNETDAEMON_H #ifdef _WIN32 #include <winsock.h> #endif #include <list> namespace zeitgeist { class TelnetSession; class TelnetServer; /** the TelnetDaemon is responsible to listen for incoming connections. For each connection it creates a new thread with a TelnetSession object, managing the session. */ class TelnetDaemon { // // types // public: protected: private: typedef std::list<TelnetSession*> TSessionList; // // functions // public: /** constructs the TelnetDaemon \param server is a reference to the server constructing this daemon */ TelnetDaemon(TelnetServer &server); virtual ~TelnetDaemon(); /** contains the code run inside the thread for this Daemon, called from the boost thread library */ void operator()(); /** called from the server to shutdown the daemon. This method destroys all session objects */ void Terminate(); /** prints the status of the daemon to stdout */ void Status(); /** adds a session a object to the list of managed sessions */ void Attach(TelnetSession *session); /** removes a session object from the list of managed sessions */ void Detach(TelnetSession *session); /** return a reference to the TelnetServer object, called by the TelnetSession objects */ const TelnetServer& GetServer() const { return mServer; } protected: /** create the network socket and start the daemon */ bool Init(int portNr); /** accepts a pending connection request, creates a new socket and associates the TelnetSession cc with it*/ bool AcceptConnection(TelnetSession& cc); // // members // public: protected: private: /** the listen socket of the server */ SOCKET mDaemonSocket; /** the local adress the daemon is bound to */ sockaddr_in mDaemonAddr; /** the list of current active sessions */ TSessionList mSessions; /** a reference to the TelnetServer */ TelnetServer &mServer; }; } #endif // ZEITGEIST_TELNETDAEMON_H --- NEW FILE: telnetsession.h --- /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- this file is part of rcssserver3D Fri May 9 2003 Copyright (C) 2002,2003 Koblenz University Copyright (C) 2003 RoboCup Soccer Server 3D Maintenance Group $Id: telnetsession.h,v 1.1 2005/12/05 21:05:01 rollmark Exp $ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. TelnetSession HISTORY: 20.06.2002 MK - initial version */ #ifndef ZEITGEIST_TELNETSESSION_H #define ZEITGEIST_TELNETSESSION_H #include <winsock.h> #include <string> #include <boost/shared_ptr.hpp> namespace zeitgeist { class ScriptServer; class TelnetDaemon; /** TelnetSession is responsible for managing one connection to a remote client. It is created by a TelnetDaemon object and runs in its own thread context. It features basic telnet session setup and option negotiation with the client, like the used echo settings. TelnetSession display a simple prompt ':', waits for the user to enter a command, and executes it using the zeitgeist ScriptServer. !TODO!: the implemented ruby commands simply output to stdout. Therefore their output is not remotely visible. Steps to do: - extend the CoreContext to provide stream or printf semantics - alter all the commands to use these output functions - implement a CoreContext that forwards the output over the network */ class TelnetSession { // // functions // public: /** constructs a TelnetSession object */ TelnetSession(TelnetDaemon &daemon); virtual ~TelnetSession(); /** receives and remebers the socket, the local adress and a reference to the ScriptServer */ void Init(SOCKET clientSocket, sockaddr_in clientAddr, boost::shared_ptr<ScriptServer> scriptServer); /** contains the code run inside the thread for this Session, called from the boost thread library */ void operator()(); /** sends a string over the connected socket, returns true on success */ bool Send(const std::string& data); /** waits for data on the connected socket and interprets special characters like backspace and return. It further provides an echo mechanism, if the client enables it. The function returns true on success with data containing the received string */ */ bool WaitForData(std::string &data); /** closes the associated socket */ void Terminate(); protected: private: /** processes special telnet control characters like del and enter */ void ProcessCommand(unsigned char command); // // members // public: protected: private: /** the connected client socket */ SOCKET mClientSocket; /** the local adress of the socket */ sockaddr_in mClientAddr; /** true, if the client enabled the echo mechanism */ bool mDoEcho; /** a reference to the daemon which created this session */ TelnetDaemon &mDaemon; /** the core context associated with this session */ boost::shared_ptr<ScriptServer> mScriptServer; }; } #endif // ZEITGEIST_TELNETSESSION_H --- NEW FILE: telnetserver_c.cpp --- #include "telnetserver.h" using namespace zeitgeist; void CLASS(TelnetServer)::DefineClass() { DEFINE_BASECLASS(zeitgeist/Node); } --- NEW FILE: telnetserver.h --- /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- this file is part of rcssserver3D Fri May 9 2003 Copyright (C) 2002,2003 Koblenz University Copyright (C) 2003 RoboCup Soccer Server 3D Maintenance Group $Id: telnetserver.h,v 1.1 2005/12/05 21:05:01 rollmark Exp $ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. TelnetServer :TODO: Class description for TelnetServer HISTORY: 20.06.2002 MK - initial version 28.06.2003 MR - modified for inclusion into the zeitgeist library */ #ifndef ZEITGEIST_TELNETSERVER_H #define ZEITGEIST_TELNETSERVER_H #ifdef _WIN32 // the socket functions require the ws2_32 library #pragma comment(lib, "ws2_32.lib") // boost threads are used for thread creation #pragma comment(lib, "libboost_thread.lib") #endif #include <zeitgeist/node.h> #include <string> namespace zeitgeist { class TelnetDaemon; /** The TelnetServer provides remote access to the Zeitgeist framework using a telnet connection. The user can interact with the system using ruby statements. The TelnetServer starts a TelnetDaemon wich listens for incoming connections and creates a new TelnetSession object for each new connection. Each TelnetSession object runs in its own thread context. */ class TelnetServer : public Node { friend class TelnetDaemon; // // functions // public: /** constructs the TelnetServer and starts the TelnetDaemons, listening on the specified port, default 23 */ TelnetServer(unsigned int port = 23); virtual ~TelnetServer(); /** prints the current status to stdout */ void Status(); /** returns the port number the daemon listens on */ unsigned int GetPort() const { return mPort; }; /** returns the hostname of this machine */ const std::string& GetHostName() const { return mHostName; } private: TelnetServer(const TelnetServer& obj); TelnetServer& operator=(const TelnetServer& obj); /** called by the daemon to tell its instance */ void SetDaemon(TelnetDaemon *daemon) { mDaemon = daemon; } /** called by the daemon to get access to the scriptserver */ boost::shared_ptr<ScriptServer> GetScriptServer() { return GetScript(); } /** starts the telnetserver, creating the daemon */ bool Start(); /** shuts the telnetserver down, returns true on clean exit */ bool Shutdown(); // // members // protected: /** the port number the daemons listens on */ unsigned int mPort; /** the instance of then daemon */ TelnetDaemon *mDaemon; /** the hostname of this machine */ std::string mHostName; }; DECLARE_CLASS(TelnetServer); } #endif //ZEITGEIST_TELNETSERVER_H --- NEW FILE: telnetserver.cpp --- #include "telnetserver.h" #include <boost/thread/thread.hpp> #include <iostream> #include "telnetdaemon.h" #include "../logserver/logserver.h" using namespace zeitgeist; using namespace std; TelnetServer::TelnetServer(unsigned int port) : Node(), mPort(port) { mDaemon = NULL; #ifdef _WIN32 // Windows socket initialization WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(1, 1); // Find winsock version if ( WSAStartup(wVersionRequested, &wsaData) ) { GetLog()->Normal() << "TelnetServer: Incorrect winsock version\n" << endl; } #endif char buffer[512]; gethostname(buffer, 511); mHostName = buffer; Start(); } TelnetServer::~TelnetServer() { Shutdown(); #ifdef _WIN32 WSACleanup(); #endif } bool TelnetServer::Start() { if (mDaemon != NULL) { Shutdown(); } // here we start the actual worker thread TelnetDaemon daemon(*this); boost::thread daemonThread(daemon); return true; } bool TelnetServer::Shutdown() { if (mDaemon != NULL) { mDaemon->Terminate(); mDaemon = NULL; } return true; } void TelnetServer::Status() { std::cout << "TelnetServer::Status()\n"; if (mDaemon == NULL) { std::cout << " No daemon running...\n"; return; } std::cout << " Daemon running on port " << GetPort() << std::endl; mDaemon->Status(); std::cout << std::endl; } |