AgentXcpp  Revision:4ac4848
Internals Documentation
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends
/home/tanjeff/projekte/agentxcpp/src/master_proxy.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright 2011 Tanjeff-Nicolai Moos <tanjeff@cccmz.de>
00003  *
00004  * This file is part of the agentXcpp library.
00005  *
00006  * AgentXcpp is free software: you can redistribute it and/or modify
00007  * it under the terms of the AgentXcpp library license, version 1, which 
00008  * consists of the GNU General Public License and some additional 
00009  * permissions.
00010  *
00011  * AgentXcpp is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * See the AgentXcpp library license in the LICENSE file of this package 
00017  * for more details.
00018  */
00019 #include <boost/bind.hpp>
00020 
00021 #include "master_proxy.hpp"
00022 #include "OpenPDU.hpp"
00023 #include "ClosePDU.hpp"
00024 #include "ResponsePDU.hpp"
00025 #include "RegisterPDU.hpp"
00026 #include "GetPDU.hpp"
00027 #include "helper.hpp"
00028 #include "types.hpp"
00029 
00030 #include <iostream>
00031 using namespace std;
00032 
00033 using namespace agentxcpp;
00034 using namespace boost;
00035 using boost::shared_ptr;
00036 
00037 
00038 
00039 
00040 
00041 
00042 master_proxy::master_proxy(boost::asio::io_service* _io_service,
00043                            std::string _description,
00044                            byte_t _default_timeout,
00045                            oid _id,
00046                            std::string _filename) :
00047     io_service(_io_service),
00048     io_service_by_user(true),
00049     socket_file(_filename.c_str()),
00050     description(_description),
00051     default_timeout(_default_timeout),
00052     id(_id)
00053 {
00054     // Initialize connector (never use timeout=0)
00055     byte_t timeout;
00056     timeout = (this->default_timeout == 0) ? 1 : this->default_timeout;
00057     connection = new connector(shared_ptr<boost::asio::io_service>(io_service),
00058                                _filename.c_str(),
00059                                timeout);
00060                       
00061     // Register this object as %PDU handler
00062     this->connection->register_handler( this );
00063 
00064     // Try to connect
00065     try
00066     {
00067         // throws disconnected:
00068         this->connect();
00069     }
00070     catch(disconnected)
00071     {
00072         // Ignore, stay disconnected
00073     }
00074 }
00075 
00076 
00077 
00078 master_proxy::master_proxy(std::string _description,
00079                            byte_t _default_timeout,
00080                            oid _id,
00081                            std::string _filename) :
00082     io_service(new boost::asio::io_service()),
00083     io_service_by_user(false),
00084     socket_file(_filename.c_str()),
00085     description(_description),
00086     default_timeout(_default_timeout),
00087     id(_id)
00088 {
00089     // Initialize connector (never use timeout=0)
00090     byte_t timeout;
00091     timeout = (this->default_timeout == 0) ? 1 : this->default_timeout;
00092     connection = new connector(shared_ptr<boost::asio::io_service>(io_service),
00093                                _filename.c_str(),
00094                                timeout);
00095                       
00096     // Register this object as %PDU handler
00097     this->connection->register_handler( this );
00098 
00099     // Try to connect
00100     try
00101     {
00102         // throws disconnected:
00103         this->connect();
00104     }
00105     catch(disconnected)
00106     {
00107         // Ignore, stay disconnected
00108     }
00109     catch(...)
00110     {
00111         // Ignore, stay disconnected
00112     }
00113     
00114     
00115 }
00116 
00117 
00118 void master_proxy::connect()
00119 {
00120     if( this->connection->is_connected() )
00121     {
00122         // we are already connected -> nothing to do
00123         return;
00124     }
00125 
00126     // Clear registrations
00127     registrations.clear();
00128 
00129     // Connect to endpoint
00130     try
00131     {
00132         this->connection->connect();
00133     }
00134     catch(boost::system::system_error)
00135     {
00136         throw;
00137     }
00138     // The response we expect from the master
00139     boost::shared_ptr<ResponsePDU> response;
00140 
00141     try
00142     {
00143         // Send OpenPDU
00144         OpenPDU openpdu;
00145         openpdu.set_timeout(default_timeout);
00146         openpdu.set_id(id);
00147         // throws disconnected and timeout_error:
00148         this->connection->send(openpdu);
00149 
00150         // Wait for response
00151         // throws disconnected and timeout_error:
00152         response = this->connection->wait_for_response(openpdu.get_packetID());
00153     }
00154     catch(disconnected)
00155     {
00156         throw; // forward
00157     }
00158     catch(timeout_error)
00159     {
00160         throw disconnected();
00161     }
00162 
00163     // Check for errors
00164     if(response->get_error() != ResponsePDU::noAgentXError)
00165     {
00166         // Some error occured, disconnect
00167         this->connection->disconnect();
00168         throw disconnected();
00169     }
00170 
00171     // All went fine, we are connected now
00172     this->sessionID = response->get_sessionID();
00173 }
00174 
00175 
00176 
00177 
00178 void master_proxy::disconnect(ClosePDU::reason_t reason)
00179 {
00180     if( ! this->connection->is_connected() )
00181     {
00182         // we are already disconnected -> nothing to do
00183         return;
00184     }
00185 
00186     // According to RFC 2741, 7.1.8. "Processing the agentx-Close-PDU", the 
00187     // master agent unregisters all MIB regions, frees all index values and all 
00188     // sysORID are removed. Thus no need to clean up before ClosePDU is sent.
00189     
00190     // The response we expect from the master
00191     boost::shared_ptr<ResponsePDU> response;
00192 
00193     // Try clean shutdown (ignore errors)
00194     try
00195     {
00196         // Unregister stuff if any
00197         std::list< boost::shared_ptr<RegisterPDU> >::const_iterator r;
00198         r = this->registrations.begin();
00199         while (r != this->registrations.end())
00200         {
00201             this->undo_registration(create_unregister_pdu(*r));
00202             r++;
00203         }
00204 
00205         // Send ClosePDU
00206         ClosePDU closepdu(this->sessionID, reason);
00207         // throws disconnected and timeout_error:
00208         this->connection->send(closepdu);
00209         
00210         // Wait for response
00211         // throws disconnected and timeout_error:
00212         response = this->connection->wait_for_response(closepdu.get_packetID());
00213     
00214         // Check for errors
00215         if(response->get_error() != ResponsePDU::noAgentXError)
00216         {
00217             // Some error occured. However, We will soon disconnect anyway.
00218             // -> ignore
00219             // TODO: Insert log message here.
00220         }
00221     }
00222     catch(...)
00223     {
00224         // We will soon disconnect anyway.
00225         // -> ignore all errors
00226     }
00227 
00228     // Finally: disconnect
00229     this->connection->disconnect();
00230 }
00231 
00232 master_proxy::~master_proxy()
00233 {
00234     // Disconnect from master agent
00235     this->disconnect(ClosePDU::reasonShutdown);
00236     
00237     // Destroy connection
00238     // Unregistering this object as %PDU handler is unneeded.
00239     delete this->connection;
00240 
00241     // Destroy io_service object if needed
00242     if( ! this->io_service_by_user )
00243     {
00244         delete this->io_service;
00245     }
00246 }
00247 
00248 
00249 void master_proxy::do_registration(boost::shared_ptr<RegisterPDU> pdu)
00250 {
00251     // Are we connected?
00252     if( ! is_connected())
00253     {
00254         throw(disconnected());
00255     }
00256 
00257     // Send RegisterPDU
00258     // (forward exceptions timeout_error and disconnected)
00259     this->connection->send(*pdu);
00260 
00261     // Wait for response
00262     // (forward exceptions timeout_error and disconnected)
00263     boost::shared_ptr<ResponsePDU> response;
00264     response = this->connection->wait_for_response(pdu->get_packetID());
00265 
00266     // Check Response
00267     switch(response->get_error())
00268     {
00269         // General errors:
00270 
00271         case ResponsePDU::parseError:
00272             // Oops, we sent a malformed PDU to the master
00273             throw(internal_error());
00274 
00275         case ResponsePDU::notOpen:
00276             // We checked the connection state before, but maybe we lost the 
00277             // connection during communication...
00278             throw(disconnected());
00279 
00280         case ResponsePDU::unsupportedContext:
00281             // We do currently not really support contexts in this library. An 
00282             // invalid context is thus probably an agentxcpp bug.
00283             throw(internal_error());
00284 
00285         case ResponsePDU::processingError:
00286             // master was unable to process the request
00287             throw(master_is_unable());
00288 
00289         case ResponsePDU::noAgentXError:
00290             // Hey, it worked!
00291             break;
00292 
00293         // Register-specific errors:
00294 
00295         case ResponsePDU::duplicateRegistration:
00296             throw(duplicate_registration());
00297 
00298         case ResponsePDU::requestDenied:
00299             throw(master_is_unwilling());
00300 
00301         default:
00302             // This is a case of can-not-happen. Probably the master is buggy.  
00303             // The agentxcpp library is bug-free of course ;-)
00304             // We throw a parse error meanwhile, because we didn't expect the 
00305             // response to look like that...
00306             throw(parse_error());
00307     }
00308 
00309     // Finish
00310     return;
00311 }
00312 
00313 
00314 
00315 
00316 
00317 void master_proxy::register_subtree(oid subtree,
00318                       byte_t priority,
00319                       byte_t timeout)
00320 {
00321     // Build PDU
00322     boost::shared_ptr<RegisterPDU> pdu(new RegisterPDU);
00323     pdu->set_subtree(subtree);
00324     pdu->set_priority(priority);
00325     pdu->set_timeout(timeout);
00326     
00327     // Sent PDU
00328     try
00329     {
00330         this->do_registration(pdu);
00331     }
00332     catch( internal_error )
00333     {
00334         // Huh, it seems that we sent a malformed PDU to the master. We convert 
00335         // this to parse_error.
00336         throw(parse_error());
00337     }
00338     catch(...)
00339     {
00340         // All other exceptions are forwarded unmodified:
00341         throw;
00342     }
00343 
00344     // Success: store registration
00345     this->registrations.push_back(pdu);
00346 
00347 }
00348 
00349 
00350 
00351 void master_proxy::unregister_subtree(oid subtree,
00352                                       byte_t priority)
00353 {
00354     // The UnregisterPDU
00355     boost::shared_ptr<UnregisterPDU> pdu;
00356 
00357     // Remove the registration from registrations list
00358     std::list< boost::shared_ptr<RegisterPDU> >::iterator r;
00359     r = this->registrations.begin();
00360     while (r != this->registrations.end())
00361     {
00362         if(   (*r)->get_priority() == priority
00363            && (*r)->get_subtree() == subtree
00364            && (*r)->get_range_subid() == 0
00365            && (*r)->get_upper_bound() == 0 )
00366         {
00367             // registration found
00368 
00369             // create UnregisterPDU
00370             pdu = create_unregister_pdu(*r);
00371 
00372             // remove registration from list, forward to next one
00373             r = registrations.erase(r);
00374         }
00375         else
00376         {
00377             // Ignore registration, inspect next one
00378             r++;
00379         }
00380     }
00381 
00382     // Sent PDU
00383     try
00384     {
00385         this->undo_registration(pdu);
00386     }
00387     catch( internal_error )
00388     {
00389         // Huh, it seems that we sent a malformed PDU to the master. We convert 
00390         // this to parse_error.
00391         throw(parse_error());
00392     }
00393     catch(...)
00394     {
00395         // All other exceptions are forwarded unmodified:
00396         throw;
00397     }
00398 }
00399 
00400 
00401 
00402 void master_proxy::undo_registration(boost::shared_ptr<UnregisterPDU> pdu)
00403 {
00404     // Are we connected?
00405     if( ! is_connected())
00406     {
00407         throw(disconnected());
00408     }
00409 
00410     // Send UnregisterPDU
00411     // (forward exceptions timeout_error and disconnected)
00412     this->connection->send(*pdu);
00413 
00414     // Wait for response
00415     // (forward exceptions timeout_error and disconnected)
00416     boost::shared_ptr<ResponsePDU> response;
00417     response = this->connection->wait_for_response(pdu->get_packetID());
00418 
00419     // Check Response
00420     switch(response->get_error())
00421     {
00422         // General errors:
00423 
00424         case ResponsePDU::parseError:
00425             // Oops, we sent a malformed PDU to the master
00426             throw(internal_error());
00427 
00428         case ResponsePDU::notOpen:
00429             // We checked the connection state before, but maybe we lost the 
00430             // connection during communication...
00431             throw(disconnected());
00432 
00433         case ResponsePDU::unsupportedContext:
00434             // We do currently not really support contexts in this library. An 
00435             // invalid context is thus probably an agentxcpp bug.
00436             throw(internal_error());
00437 
00438         case ResponsePDU::processingError:
00439             // master was unable to process the request
00440             throw(master_is_unable());
00441 
00442         case ResponsePDU::noAgentXError:
00443             // Hey, it worked!
00444             break;
00445 
00446         // Register-specific errors:
00447 
00448         case ResponsePDU::unknownRegistration:
00449             throw(unknown_registration());
00450 
00451         default:
00452             // This is a case of can-not-happen. Probably the master is buggy.  
00453             // The agentxcpp library is bug-free of course ;-)
00454             // We throw a parse error meanwhile, because we didn't expect the 
00455             // response to look like that...
00456             throw(parse_error());
00457     }
00458 
00459     // Finish
00460     return;
00461 }
00462 
00463 
00464 
00465 boost::shared_ptr<UnregisterPDU> master_proxy::create_unregister_pdu(
00466                                     boost::shared_ptr<RegisterPDU> pdu)
00467 {
00468     boost::shared_ptr<UnregisterPDU> new_pdu(new UnregisterPDU());
00469     new_pdu->set_subtree( pdu->get_subtree() );
00470     new_pdu->set_range_subid( pdu->get_range_subid() );
00471     new_pdu->set_upper_bound( pdu->get_upper_bound() );
00472     new_pdu->set_priority( pdu->get_priority() );
00473 
00474     return new_pdu;
00475 }
00476