00001 /* 00002 * Copyright 2011-2012 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 #ifndef _MASTER_PROXY_H_ 00020 #define _MASTER_PROXY_H_ 00021 00022 #include <boost/asio.hpp> 00023 00024 #include <fstream> 00025 #include <string> 00026 #include <map> 00027 #include <list> 00028 #include <boost/shared_ptr.hpp> 00029 #include "types.hpp" 00030 #include "oid.hpp" 00031 #include "variable.hpp" 00032 #include "ClosePDU.hpp" 00033 #include "ResponsePDU.hpp" 00034 #include "RegisterPDU.hpp" 00035 #include "UnregisterPDU.hpp" 00036 #include "connector.hpp" 00037 00038 namespace agentxcpp 00039 { 00040 /** 00041 * \brief This class represents the master agent in a subagent program. 00042 * 00043 * \par Introduction 00044 * 00045 * This class is used on the subagent's side of a connection between 00046 * subagent and master agent. It serves as a proxy which represents the 00047 * master agent. It is possible for a subagent to hold connections to more 00048 * than one master agents. For each connection one master_proxy object is 00049 * created. Multiple connections to the same master agent are possible, 00050 * too, in which case one master_proxy per connection is needed. 00051 */ 00052 /** 00053 * \par Connection State 00054 * 00055 * The master_proxy is always in one of the following states: 00056 * -# connected 00057 * -# disconnected 00058 * . 00059 * The session to the master agent is established when creating a 00060 * master_proxy object, thus the object usually starts in connected state. 00061 * If that fails, the object starts in disconnected state. A connected 00062 * master_proxy object may also loose the connection to the master agent 00063 * and consequently become disconnected, without informing the user. It is 00064 * possible to re-connect with the reconnect() function at any time (even 00065 * if the session is currently established - it will be shut down and 00066 * re-established in this case). When the object is destroyed, the session 00067 * will be cleanly shut down. The connection state can be inspected with 00068 * the is_connected() function. Some functions throw a disconnected 00069 * exception if the session is not currently established. 00070 * 00071 */ 00072 /** 00073 * \par The io_service object 00074 * 00075 * The master_proxy class uses the boost::asio library for networking and 00076 * therefore needs a boost::asio::io_service object. This object can either 00077 * be provided by the user or created automatically. There are two 00078 * constructors available therefore. The used object (whether auto-created 00079 * or not) can be obtained using the get_io_service() function. If the 00080 * io_service object was autocreated by a constructor, it will be destroyed 00081 * by the destructor. If the user provided the io_service, it will NOT be 00082 * destroyed by the destructor. It is possible to create multiple 00083 * master_proxy objects using the same io_service object, or using 00084 * different io_service objects. 00085 * 00086 * Receiving data from the master agent is done asynchronously and only 00087 * works if io_service::run() or io_service::run_one() is invoked. 00088 * However, some operations (such as registering stuff) invoke 00089 * io_service::run_one() several times while waiting for a response from 00090 * the master agent. If the io_service object is not used exclusively by 00091 * the master_proxy object (which is entirely possible), this may complete 00092 * asynchronous events before the library operation (e.g. registering) is 00093 * completed. Even the internal asynchronous reception calls 00094 * io_service::run_one() while waiting for more data. If this behaviour is 00095 * not desired, a separate io_service object should be used for other 00096 * asynchronous I/O operations. 00097 * 00098 */ 00099 /** 00100 * \par Registrations 00101 * 00102 * Before the master agent sends requests to a subagent, the subagent must 00103 * register a subtree. Doing so informs the master agent that the subagent 00104 * wishes to handle requests for these OIDs. A subtree is an OID which 00105 * denotes the root of a subtree in which some of the offered objects 00106 * resides. For example, when two objects shall be offered with the OIDs 00107 * 1.3.6.1.4.1.42<b>.1.1</b> and 1.3.6.1.4.1.42<b>.1.2</b>, then a subtree 00108 * with OID 1.3.6.1.4.1.42<b>.1</b> should be registered, which includes 00109 * both objects. The master agent will then forward all requests 00110 * conecerning objects in this subtree to this subagent. Requests to 00111 * non-existing objects (e.g. 1.3.6.1.4.1.42<b>.1.3</b>) are also 00112 * forwarded, and the agentXcpp library will take care of them and return 00113 * an appropriate error to the master agent. 00114 * 00115 * The function register_subtree() is used to register a subtree. It is 00116 * typically called for the highest-level OID of the MIB which is 00117 * implemented by the subagent. However, it is entirely possible to 00118 * register multiple subtrees. 00119 * 00120 * Identical subtrees are subtrees with the exact same root OID. Each 00121 * registration is done with a priority value. The higher the value, the 00122 * lower the priority. When identical subtrees are registered (by the same 00123 * subagent or by different subagents), the priority value is used to 00124 * decide which subagent gets the requests. The master refuses identical 00125 * registrations with the same priority values. Note however, that in case 00126 * of overlapping subtrees which are \e not identical (e.g. 00127 * 1.3.6.1.4.1.42<b>.1</b> and 1.3.6.1.4.1.42<b>.1.33.1</b>), the most 00128 * specific subtree (i.e. the one with the longest OID) wins regardless of 00129 * the priority values. 00130 * 00131 * It is also possible to unregister subtrees using the 00132 * unregister_subtree() function. This informs the master agent that the 00133 * MIB region is no longer available from this subagent. 00134 * 00135 * \internal 00136 * 00137 * The master_proxy object generates a RegisterPDU object each time a 00138 * registration is performed. These RegisterPDU objects are stored in the 00139 * registrations member. 00140 * 00141 * When unregistering, the matching RegisterPDU is removed from the 00142 * registration member. 00143 * 00144 * The registration member becomes invalid on connection loss. Since a 00145 * connection loss is not signalled, the member cannot be cleared in such 00146 * situations. Therefore, it is cleared in the connect() method if the 00147 * object is currently disconnected. If connect() is called on a connected 00148 * master_proxy object, the registrations member is not cleared. 00149 * 00150 * \endinternal 00151 * 00152 */ 00153 /** 00154 * \par Adding and Removing Variables 00155 * 00156 * Registering a subtree does not make any SNMP variables accessible yet. 00157 * To provide SNMP variables, they must be added to the master_proxy 00158 * object, e.g. using add_variable(). The master_proxy object will then 00159 * dispatch incoming requests to the variables it knows about. If a request 00160 * is received for an OID for which no variable has been added, an 00161 * appropriate error is returned to the master agent. 00162 * 00163 * Added variables can be removed again using remove_variable(). This makes 00164 * a variable inaccessible for the master agent. 00165 * 00166 * \internal 00167 * 00168 * The variables are stored in the member variables, which is a 00169 * std::map<oid, shared_ptr<variable> >. The key is the OID for which the 00170 * variable was added. This allows easy lookup for the request 00171 * dispatcher. 00172 * 00173 * When removing a variable, it is removed from the variables member. 00174 * 00175 * The variables member becomes invalid on connection loss. Since a 00176 * connection loss is not signalled, the member cannot be cleared in such 00177 * situations. Therefore, it is cleared in the connect() method if the 00178 * object is currently disconnected. If connect() is called on a connected 00179 * master_proxy object, the variables member is not cleared. 00180 * 00181 * \endinternal 00182 * 00183 */ 00184 /** 00185 * \internal 00186 * 00187 * \par Internals 00188 * 00189 * The io_service_by_user variable is used to store whether the io_service 00190 * object was generated automatically. It is set to true or false by the 00191 * respective constructors and evaluated by the destructor. 00192 * 00193 * Receiving and processing PDU's coming from the master is done using the 00194 * connector class. The master_proxy class implements the 00195 * connectro::pdu_handler interface to recieve PDU's from the connector. A 00196 * master_proxy object registers itself with the connector object, which 00197 * then invokes master_proxy::handle_pdu() for incoming PDU's. Registering 00198 * is done by the constructors, while the destructor unregisters the 00199 * object. 00200 */ 00201 // TODO: describe timeout handling 00202 // TODO: byte ordering is constant for a session. See rfc 2741, 7.1.1 00203 class master_proxy : public connector::pdu_handler 00204 { 00205 private: 00206 00207 /** 00208 * \brief The mandatory io_service object. 00209 * 00210 * This object is needed for boost::asio operation. Depending on 00211 * the constructor used, the object is either provided by the user 00212 * or generated automatically. 00213 */ 00214 // TODO: use shared_ptr<> 00215 boost::asio::io_service* io_service; 00216 00217 /** 00218 * \brief Was the io_service object provided by the user? 00219 */ 00220 bool io_service_by_user; 00221 00222 /** 00223 * \brief The path to the unix domain socket. 00224 */ 00225 std::string socket_file; 00226 00227 /** 00228 * \brief The connector object used for networking. 00229 * 00230 * Created by constructors, destroyed by destructor. 00231 */ 00232 connector* connection; 00233 00234 /** 00235 * \brief The session ID of the current session. 00236 * 00237 * If disconnected, the value is undefined. 00238 */ 00239 uint32_t sessionID; 00240 00241 /** 00242 * \brief A string describing the subagent. 00243 * 00244 * Set upon object creation. It is allowed to be emtpy. 00245 */ 00246 std::string description; 00247 00248 /** 00249 * \brief Default timeout of the session (in seconds). 00250 * 00251 * A value of 0 indicates that there is no session-wide default. 00252 */ 00253 byte_t default_timeout; 00254 00255 /** 00256 * \brief An Object Identifier that identifies the subagent. May be 00257 * the null OID. 00258 */ 00259 oid id; 00260 00261 /** 00262 * \brief The registrations. 00263 * 00264 * Every time an registration is performed, the RegisterPDUs is 00265 * stored in this list. This allows to automatically re-register 00266 * these subtrees on reconnect (e.g. after a connection loss). 00267 */ 00268 std::list< boost::shared_ptr<RegisterPDU> > registrations; 00269 00270 /** 00271 * \brief Storage for all SNMP variables known to the agentXcpp 00272 * library. 00273 */ 00274 std::map< oid, shared_ptr<variable> > variables; 00275 00276 /** 00277 * \brief Send a RegisterPDU to the master agent. 00278 * 00279 * This function sends a RegisterPDU to the master agent, waites 00280 * for the response and evaluates it. This means that run_one() is 00281 * called one or more times on the io_service object. 00282 * 00283 * \param pdu The RegisterPDU to send. 00284 * 00285 * \exception disconnected If the master_proxy is currently in 00286 * state 'disconnected'. 00287 * 00288 * \exception timeout_error If the master agent does not 00289 * respond within the timeout interval. 00290 * 00291 * \exception internal_error If the master received a malformed 00292 * PDU. This is probably a programming 00293 * error within the agentXcpp library. 00294 * 00295 * \exception master_is_unable The master agent was unable to 00296 * perform the desired register 00297 * request. The reason for that is 00298 * unknown. 00299 * 00300 * \exception duplicate_registration If the exact same subtree was 00301 * alread registered, either by 00302 * another subagent or by this 00303 * subagent. 00304 * 00305 * \exception master_is_unwilling If the master was unwilling for 00306 * some reason to make the desired 00307 * registration. 00308 * 00309 * \exception parse_error If an unexpected response was received 00310 * from the master. This is probably a 00311 * programming error within the master 00312 * agent. It is possible that the master 00313 * actually performed the desired 00314 * registration and that a retry will result 00315 * in a duplicate_registration error. 00316 */ 00317 void do_registration(boost::shared_ptr<RegisterPDU> pdu); 00318 00319 /** 00320 * \brief Send a UnregisterPDU to the master agent. 00321 * 00322 * This function sends an UnregisterPDU to the master agent which 00323 * revokes the registration done by a RegisterPDU. Then it waites 00324 * for the response and evaluates it. This means that run_one() is 00325 * called one or more times on the io_service object. 00326 * 00327 * \param pdu The RegisterPDU whose registration is to be revoked. 00328 * 00329 * \exception disconnected If the master_proxy is currently in 00330 * state 'disconnected'. 00331 * 00332 * \exception timeout_error If the master agent does not 00333 * respond within the timeout interval. 00334 * 00335 * \exception internal_error If the master received a malformed 00336 * PDU. This is probably a programming 00337 * error within the agentXcpp library. 00338 * 00339 * \exception master_is_unable The master agent was unable to 00340 * perform the desired register 00341 * request. The reason for that is 00342 * unknown. 00343 * 00344 * \exception unknown_registration The MIB region is not currently 00345 * registered with this parameters. 00346 * 00347 * \exception parse_error If an unexpected response was received 00348 * from the master. This is probably a 00349 * programming error within the master 00350 * agent. It is possible that the master 00351 * actually performed the desired 00352 * registration and that a retry will result 00353 * in a duplicate_registration error. 00354 */ 00355 void undo_registration(boost::shared_ptr<UnregisterPDU> pdu); 00356 00357 /** 00358 * \brief Create UnregisterPDU for undoing a registration. 00359 * 00360 * This function creates an UnregisterPDU which unregisters the MIB 00361 * region which is registered by a given RegisterPDU. 00362 * 00363 * \param pdu The RegisterPDU which creates a registration. 00364 * 00365 * \return The created UnregisterPDU. 00366 * 00367 * \exception None. 00368 */ 00369 boost::shared_ptr<UnregisterPDU> create_unregister_pdu( 00370 boost::shared_ptr<RegisterPDU> pdu); 00371 00372 00373 public: 00374 /** 00375 * \internal 00376 * 00377 * \brief The dispatcher for incoming %PDU's. 00378 * 00379 * This method implements pdu_handler::handle_pdu() and is invoked 00380 * by the connector object when PDU's are received. 00381 */ 00382 virtual void handle_pdu(shared_ptr<PDU>, int error); 00383 00384 /** 00385 * \brief Create a session object connected via unix domain 00386 * socket 00387 * 00388 * This constructor tries to connect to the master agent. If that 00389 * fails, the object is created nevertheless and will be in state 00390 * disconnected. 00391 * 00392 * This constructor takes an io_service object as parameter. The 00393 * io_service is not destroyed by the constructor. 00394 * 00395 * \param io_service The io_service object. 00396 * 00397 * \param description A string describing the subagent. This 00398 * description cannot be changed later. 00399 * 00400 * \param default_timeout The length of time, in seconds, that 00401 * the master agent should allow to elapse 00402 * before it regards the subagent as not 00403 * responding. The value is also used when 00404 * waiting synchronously for data from the 00405 * master agent (e.g. when registering 00406 * stuff). Allowed values are 0-255, with 0 00407 * meaning "no default for this session". 00408 * 00409 * \param ID An Object Identifier that identifies the subagent. 00410 * Default is the null OID (no ID). 00411 * 00412 * \param unix_domain_socket The socket file to connect to. 00413 * Defaults to /var/agentx/master, as 00414 * desribed in RFC 2741, section 8.2.1 00415 * "Well-known Values". 00416 */ 00417 master_proxy(boost::asio::io_service* io_service, 00418 std::string description="", 00419 byte_t default_timeout=0, 00420 oid ID=oid(), 00421 std::string unix_domain_socket="/var/agentx/master"); 00422 00423 /** 00424 * \brief Create a session object connected via unix domain 00425 * socket. 00426 * 00427 * This constructor tries to connect to the master agent. If that 00428 * fails, the object is created nevertheless and will be in state 00429 * disconnected. 00430 * 00431 * This constructor creates an io_service object which is destroyed 00432 * by the desctructor. 00433 * 00434 * \param description A string describing the subagent. This 00435 * description cannot be changed later. 00436 * 00437 * \param default_timeout The length of time, in seconds, that 00438 * the master agent should allow to elapse 00439 * before it regards the subagent as not 00440 * responding. The value is also used when 00441 * waiting synchronously for data from the 00442 * master agent (e.g. when registering 00443 * stuff). Allowed values are 0-255, with 0 00444 * meaning "no default for this session". 00445 * 00446 * \param ID An Object Identifier that identifies the subagent. 00447 * Default is the null OID (no ID). 00448 * 00449 * \param unix_domain_socket The socket file to connect to. 00450 * Defaults to /var/agentx/master, as 00451 * desribed in RFC 2741, section 8.2.1 00452 * "Well-known Values". 00453 */ 00454 master_proxy(std::string description="", 00455 byte_t default_timeout=0, 00456 oid ID=oid(), 00457 std::string unix_domain_socket="/var/agentx/master"); 00458 00459 /** 00460 * \brief Register a subtree with the master agent 00461 * 00462 * This function registers a subtree (or MIB region). 00463 * 00464 * \internal 00465 * 00466 * This method adds the registered subtree to registrations on 00467 * success. 00468 * 00469 * \endinternal 00470 * 00471 * \param subtree The (root of the) subtree to register. 00472 * 00473 * \param priority The priority with which to register the subtree. 00474 * Default is 127 according to RFC 2741, 6.2.3. 00475 * "The agentx-Register-PDU". 00476 * 00477 * \param timeout The timeout value for the registered subtree, in 00478 * seconds. This value overrides the timeout of the 00479 * session. Default value is 0 (no override) 00480 * according to RFC 2741, 6.2.3. "The 00481 * agentx-Register-PDU". 00482 * 00483 * \exception disconnected If the master_proxy is currently in 00484 * state 'disconnected'. 00485 * 00486 * \exception timeout_exception If the master agent does not 00487 * respond within the timeout 00488 * interval. 00489 * 00490 * \exception master_is_unable The master agent was unable to 00491 * perform the desired register 00492 * request. The reason for that is 00493 * unknown. 00494 * 00495 * \exception duplicate_registration If the exact same subtree was 00496 * alread registered, either by 00497 * another subagent or by this 00498 * subagent. 00499 * 00500 * \exception master_is_unwilling If the master was unwilling for 00501 * some reason to make the desired 00502 * registration. 00503 * 00504 * \exception parse_error A malformed network message was found 00505 * during communcation with the master. This 00506 * may be a programming error in the master 00507 * or in the agentXcpp library. It is 00508 * possible that the master actually 00509 * performed the desired registration and 00510 * that a retry will result in a 00511 * duplicate_registration error. 00512 * 00513 * \note This function invokes run_one() one or more times on the 00514 * io_service object. 00515 */ 00516 void register_subtree(oid subtree, 00517 byte_t priority=127, 00518 byte_t timeout=0); 00519 00520 /** 00521 * \brief Unregister a subtree with the master agent 00522 * 00523 * This function unregisters a subtree (or MIB region) which has 00524 * previously been registered. 00525 * 00526 * \internal 00527 * 00528 * This method removes the registered subtree from 00529 * registrations. 00530 * 00531 * \endinternal 00532 * 00533 * \param subtree The (root of the) subtree to unregister. 00534 * 00535 * \param priority The priority with which the registration was 00536 * done. 00537 * 00538 * \exception disconnected If the master_proxy is currently in 00539 * state 'disconnected'. 00540 * 00541 * \exception timeout_error If the master agent does not 00542 * respond within the timeout interval. 00543 * 00544 * \exception master_is_unable The master agent was unable to 00545 * perform the desired unregister 00546 * request. The reason for that is 00547 * unknown. 00548 * 00549 * \exception unknown_registration The MIB region is not currently 00550 * registered with this parameters. 00551 * 00552 * \exception parse_error A malformed network message was found 00553 * during communcation with the master. This 00554 * may be a programming error in the master 00555 * or in the agentXcpp library. It is 00556 * possible that the master actually 00557 * unregistered the MIB region. 00558 * 00559 * \note This function invokes run_one() one or more times on the 00560 * io_service object. 00561 */ 00562 // TODO: the 'priority' parameter can possibly be omitted: the 00563 // value can be stored by master_agent upon subtree registration. 00564 void unregister_subtree(oid subtree, 00565 byte_t priority=127); 00566 00567 /** 00568 * \brief Get the io_service object used by this master_proxy. 00569 */ 00570 boost::asio::io_service* get_io_service() const 00571 { 00572 return this->io_service; 00573 } 00574 00575 /** 00576 * \brief Check whether the session is in state connected 00577 * 00578 * \returns true if the session is connected, false otherwise. 00579 */ 00580 bool is_connected() 00581 { 00582 return this->connection->is_connected(); 00583 } 00584 00585 /** 00586 * \brief Connect to the master agent. 00587 * 00588 * \note Upon creation of a session object, the connection is 00589 * automatically established. If the current state is 00590 * "connected", the function does nothing. 00591 * 00592 * \exception disconnected If connecting fails. 00593 */ 00594 void connect(); 00595 00596 /** 00597 * \brief Shutdown the session. 00598 * 00599 * Disconnect from the master agent. 00600 * 00601 * \note Upon destruction of a session object the session is 00602 * automatically shutdown. If the session is in state 00603 * "disconnected", this function does nothing. 00604 * 00605 * \param reason The shutdown reason is reported to the master 00606 * agent during shutdown. 00607 * 00608 * \exception None. 00609 */ 00610 void disconnect(ClosePDU::reason_t reason=ClosePDU::reasonShutdown); 00611 00612 /** 00613 * \brief Reconnect to the master agent. 00614 * 00615 * Disconnects from the master (only if currently connected), then 00616 * connects again. 00617 * 00618 * \exception disconnected If connecting fails. 00619 */ 00620 void reconnect() 00621 { 00622 this->connection->disconnect(); 00623 // throws disconnected, which is forwarded: 00624 this->connection->connect(); 00625 } 00626 00627 /** 00628 * \brief Get the sessionID of the session 00629 * 00630 * Get the session ID of the last established session, even if the 00631 * current state is "disconnected". 00632 * 00633 * \return The session ID of the last established session. If 00634 * object was never connected to the master, 0 is 00635 * returned. 00636 */ 00637 uint32_t get_sessionID() 00638 { 00639 return this->sessionID; 00640 } 00641 00642 /** 00643 * \brief Destructor. 00644 * 00645 * The destructor cleanly shuts down the session with the reason 00646 * 'Shutdown' (if it is currently established) and destroys the 00647 * master_proxy object. It also destroys the io_service object if 00648 * it was created automatically (i.e. not provided by the user). 00649 */ 00650 ~master_proxy(); 00651 00652 /** 00653 * \brief Add an SNMP variable for serving. 00654 * 00655 * This adds an SNMP variable which can then be read and/or 00656 * written. 00657 * 00658 * Variables can only be added to MIB regions which were registered 00659 * in advance. 00660 * 00661 * If adding a variable with an id for which another variable is 00662 * already registered, it replaces the odl one. 00663 * 00664 * \param id The OID of the variable. 00665 * 00666 * \param v The variable. 00667 * 00668 * \exception unknown_registration If trying to add a variable 00669 * with an id which does not reside 00670 * within a registered MIB 00671 * region. 00672 */ 00673 void add_variable(const oid& id, shared_ptr<variable> v); 00674 00675 /** 00676 * \brief Remove an SNMP variable so that is not longer accessible. 00677 * 00678 * This removes a variable previously added using add_variable(). 00679 * The variable will no longer receive SNMP requests. 00680 * 00681 * If no variable is known for the given id, nothing happens. 00682 * 00683 * \param id The OID of the variable to remove. This is the OID 00684 * which was given to add_variable(). 00685 * 00686 * \exception None. 00687 */ 00688 void remove_variable(const oid& id); 00689 }; 00690 } 00691 00692 #endif