AgentXcpp  Revision:0.1.1
Internals Documentation
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Pages
master_proxy.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2012 Tanjeff-Nicolai Moos <tanjeff@cccmz.de>
3  *
4  * This file is part of the agentXcpp library.
5  *
6  * AgentXcpp is free software: you can redistribute it and/or modify
7  * it under the terms of the AgentXcpp library license, version 1, which
8  * consists of the GNU General Public License and some additional
9  * permissions.
10  *
11  * AgentXcpp is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * See the AgentXcpp library license in the LICENSE file of this package
17  * for more details.
18  */
19 #ifndef _MASTER_PROXY_H_
20 #define _MASTER_PROXY_H_
21 
22 #include <fstream>
23 #include <string>
24 #include <map>
25 #include <list>
26 
27 #include <boost/shared_ptr.hpp>
28 #include <boost/asio.hpp>
29 #include <boost/cstdint.hpp>
30 
31 #include "oid.hpp"
32 #include "variable.hpp"
33 #include "ClosePDU.hpp"
34 #include "ResponsePDU.hpp"
35 #include "RegisterPDU.hpp"
36 #include "UnregisterPDU.hpp"
37 #include "connector.hpp"
38 
39 using boost::uint8_t;
40 using boost::uint32_t;
41 
42 namespace agentxcpp
43 {
44  /**
45  * \brief This class represents the master agent in a subagent program.
46  *
47  * \par Introduction
48  *
49  * This class is used on the subagent's side of a connection between
50  * subagent and master agent. It serves as a proxy which represents the
51  * master agent. It is possible for a subagent to hold connections to more
52  * than one master agents. For each connection one master_proxy object is
53  * created. Multiple connections to the same master agent are possible,
54  * too, in which case one master_proxy per connection is needed.
55  */
56  /**
57  * \par Connection State
58  *
59  * The master_proxy is always in one of the following states:
60  * -# connected
61  * -# disconnected
62  * .
63  * The session to the master agent is established when creating a
64  * master_proxy object, thus the object usually starts in connected state.
65  * If that fails, the object starts in disconnected state. A connected
66  * master_proxy object may also loose the connection to the master agent
67  * and consequently become disconnected, without informing the user. It is
68  * possible to re-connect with the reconnect() function at any time (even
69  * if the session is currently established - it will be shut down and
70  * re-established in this case). When the object is destroyed, the session
71  * will be cleanly shut down. The connection state can be inspected with
72  * the is_connected() function. Some functions throw a disconnected
73  * exception if the session is not currently established.
74  *
75  */
76  /**
77  * \par The io_service object
78  *
79  * The master_proxy class uses the boost::asio library for networking and
80  * therefore needs a boost::asio::io_service object. This object can either
81  * be provided by the user or created automatically. There are two
82  * constructors available therefore. The used object (whether auto-created
83  * or not) can be obtained using the get_io_service() function. If the
84  * io_service object was autocreated by a constructor, it will be destroyed
85  * by the destructor. If the user provided the io_service, it will NOT be
86  * destroyed by the destructor. It is possible to create multiple
87  * master_proxy objects using the same io_service object, or using
88  * different io_service objects.
89  *
90  * Receiving data from the master agent is done asynchronously and only
91  * works if io_service::run() or io_service::run_one() is invoked.
92  * However, some operations (such as registering stuff) invoke
93  * io_service::run_one() several times while waiting for a response from
94  * the master agent. If the io_service object is not used exclusively by
95  * the master_proxy object (which is entirely possible), this may complete
96  * asynchronous events before the library operation (e.g. registering) is
97  * completed. Even the internal asynchronous reception calls
98  * io_service::run_one() while waiting for more data. If this behaviour is
99  * not desired, a separate io_service object should be used for other
100  * asynchronous I/O operations.
101  *
102  */
103  /**
104  * \par Registrations
105  *
106  * Before the master agent sends requests to a subagent, the subagent must
107  * register a subtree. Doing so informs the master agent that the subagent
108  * wishes to handle requests for these OIDs. A subtree is an OID which
109  * denotes the root of a subtree in which some of the offered objects
110  * resides. For example, when two objects shall be offered with the OIDs
111  * 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
112  * with OID 1.3.6.1.4.1.42<b>.1</b> should be registered, which includes
113  * both objects. The master agent will then forward all requests
114  * conecerning objects in this subtree to this subagent. Requests to
115  * non-existing objects (e.g. 1.3.6.1.4.1.42<b>.1.3</b>) are also
116  * forwarded, and the agentXcpp library will take care of them and return
117  * an appropriate error to the master agent.
118  *
119  * The function register_subtree() is used to register a subtree. It is
120  * typically called for the highest-level OID of the MIB which is
121  * implemented by the subagent. However, it is entirely possible to
122  * register multiple subtrees.
123  *
124  * Identical subtrees are subtrees with the exact same root OID. Each
125  * registration is done with a priority value. The higher the value, the
126  * lower the priority. When identical subtrees are registered (by the same
127  * subagent or by different subagents), the priority value is used to
128  * decide which subagent gets the requests. The master refuses identical
129  * registrations with the same priority values. Note however, that in case
130  * of overlapping subtrees which are \e not identical (e.g.
131  * 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
132  * specific subtree (i.e. the one with the longest OID) wins regardless of
133  * the priority values.
134  *
135  * It is also possible to unregister subtrees using the
136  * unregister_subtree() function. This informs the master agent that the
137  * MIB region is no longer available from this subagent.
138  *
139  * \internal
140  *
141  * The master_proxy object generates a RegisterPDU object each time a
142  * registration is performed. These RegisterPDU objects are stored in the
143  * registrations member.
144  *
145  * When unregistering, the matching RegisterPDU is removed from the
146  * registration member.
147  *
148  * The registration member becomes invalid on connection loss. Since a
149  * connection loss is not signalled, the member cannot be cleared in such
150  * situations. Therefore, it is cleared in the connect() method if the
151  * object is currently disconnected. If connect() is called on a connected
152  * master_proxy object, the registrations member is not cleared.
153  *
154  * \endinternal
155  *
156  */
157  /**
158  * \par Adding and Removing Variables
159  *
160  * Registering a subtree does not make any SNMP variables accessible yet.
161  * To provide SNMP variables, they must be added to the master_proxy
162  * object, e.g. using add_variable(). The master_proxy object will then
163  * dispatch incoming requests to the variables it knows about. If a request
164  * is received for an OID for which no variable has been added, an
165  * appropriate error is returned to the master agent.
166  *
167  * Added variables can be removed again using remove_variable(). This makes
168  * a variable inaccessible for the master agent.
169  *
170  * \internal
171  *
172  * The variables are stored in the member variables, which is a
173  * std::map<oid, shared_ptr<variable> >. The key is the OID for which the
174  * variable was added. This allows easy lookup for the request
175  * dispatcher.
176  *
177  * When removing a variable, it is removed from the variables member.
178  *
179  * The variables member becomes invalid on connection loss. Since a
180  * connection loss is not signalled, the member cannot be cleared in such
181  * situations. Therefore, it is cleared in the connect() method if the
182  * object is currently disconnected. If connect() is called on a connected
183  * master_proxy object, the variables member is not cleared.
184  *
185  * \endinternal
186  *
187  */
188  /**
189  * \internal
190  *
191  * \par Internals
192  *
193  * The io_service_by_user variable is used to store whether the io_service
194  * object was generated automatically. It is set to true or false by the
195  * respective constructors and evaluated by the destructor.
196  *
197  * Receiving and processing PDU's coming from the master is done using the
198  * connector class. The master_proxy class implements the
199  * connectro::pdu_handler interface to recieve PDU's from the connector. A
200  * master_proxy object registers itself with the connector object, which
201  * then invokes master_proxy::handle_pdu() for incoming PDU's. Registering
202  * is done by the constructors, while the destructor unregisters the
203  * object.
204  */
205  // TODO: describe timeout handling
206  // TODO: byte ordering is constant for a session. See rfc 2741, 7.1.1
208  {
209  private:
210 
211  /**
212  * \brief The mandatory io_service object.
213  *
214  * This object is needed for boost::asio operation. Depending on
215  * the constructor used, the object is either provided by the user
216  * or generated automatically.
217  */
218  boost::asio::io_service* io_service;
219 
220  /**
221  * \brief Was the io_service object provided by the user?
222  */
224 
225  /**
226  * \brief The path to the unix domain socket.
227  */
228  std::string socket_file;
229 
230  /**
231  * \brief The connector object used for networking.
232  *
233  * Created by constructors, destroyed by destructor.
234  */
236 
237  /**
238  * \brief The session ID of the current session.
239  *
240  * If disconnected, the value is undefined.
241  */
242  uint32_t sessionID;
243 
244  /**
245  * \brief A string describing the subagent.
246  *
247  * Set upon object creation. It is allowed to be emtpy.
248  */
249  std::string description;
250 
251  /**
252  * \brief Default timeout of the session (in seconds).
253  *
254  * A value of 0 indicates that there is no session-wide default.
255  */
257 
258  /**
259  * \brief An Object Identifier that identifies the subagent. May be
260  * the null OID.
261  */
263 
264  /**
265  * \brief The registrations.
266  *
267  * Every time an registration is performed, the RegisterPDUs is
268  * stored in this list. This allows to automatically re-register
269  * these subtrees on reconnect (e.g. after a connection loss).
270  */
271  std::list< boost::shared_ptr<RegisterPDU> > registrations;
272 
273  /**
274  * \brief Storage for all SNMP variables known to the agentXcpp
275  * library.
276  */
277  std::map< oid, shared_ptr<variable> > variables;
278 
279  /**
280  * \brief Send a RegisterPDU to the master agent.
281  *
282  * This function sends a RegisterPDU to the master agent, waites
283  * for the response and evaluates it. This means that run_one() is
284  * called one or more times on the io_service object.
285  *
286  * \param pdu The RegisterPDU to send.
287  *
288  * \exception disconnected If the master_proxy is currently in
289  * state 'disconnected'.
290  *
291  * \exception timeout_error If the master agent does not
292  * respond within the timeout interval.
293  *
294  * \exception internal_error If the master received a malformed
295  * PDU. This is probably a programming
296  * error within the agentXcpp library.
297  *
298  * \exception master_is_unable The master agent was unable to
299  * perform the desired register
300  * request. The reason for that is
301  * unknown.
302  *
303  * \exception duplicate_registration If the exact same subtree was
304  * alread registered, either by
305  * another subagent or by this
306  * subagent.
307  *
308  * \exception master_is_unwilling If the master was unwilling for
309  * some reason to make the desired
310  * registration.
311  *
312  * \exception parse_error If an unexpected response was received
313  * from the master. This is probably a
314  * programming error within the master
315  * agent. It is possible that the master
316  * actually performed the desired
317  * registration and that a retry will result
318  * in a duplicate_registration error.
319  */
320  void do_registration(boost::shared_ptr<RegisterPDU> pdu);
321 
322  /**
323  * \brief Send a UnregisterPDU to the master agent.
324  *
325  * This function sends an UnregisterPDU to the master agent which
326  * revokes the registration done by a RegisterPDU. Then it waites
327  * for the response and evaluates it. This means that run_one() is
328  * called one or more times on the io_service object.
329  *
330  * \param pdu The RegisterPDU whose registration is to be revoked.
331  *
332  * \exception disconnected If the master_proxy is currently in
333  * state 'disconnected'.
334  *
335  * \exception timeout_error If the master agent does not
336  * respond within the timeout interval.
337  *
338  * \exception internal_error If the master received a malformed
339  * PDU. This is probably a programming
340  * error within the agentXcpp library.
341  *
342  * \exception master_is_unable The master agent was unable to
343  * perform the desired register
344  * request. The reason for that is
345  * unknown.
346  *
347  * \exception unknown_registration The MIB region is not currently
348  * registered with this parameters.
349  *
350  * \exception parse_error If an unexpected response was received
351  * from the master. This is probably a
352  * programming error within the master
353  * agent. It is possible that the master
354  * actually performed the desired
355  * registration and that a retry will result
356  * in a duplicate_registration error.
357  */
358  void undo_registration(boost::shared_ptr<UnregisterPDU> pdu);
359 
360  /**
361  * \brief Create UnregisterPDU for undoing a registration.
362  *
363  * This function creates an UnregisterPDU which unregisters the MIB
364  * region which is registered by a given RegisterPDU.
365  *
366  * \param pdu The RegisterPDU which creates a registration.
367  *
368  * \return The created UnregisterPDU.
369  *
370  * \exception None.
371  */
372  boost::shared_ptr<UnregisterPDU> create_unregister_pdu(
373  boost::shared_ptr<RegisterPDU> pdu);
374 
375 
376  public:
377  /**
378  * \internal
379  *
380  * \brief The dispatcher for incoming %PDU's.
381  *
382  * This method implements pdu_handler::handle_pdu() and is invoked
383  * by the connector object when PDU's are received.
384  */
385  virtual void handle_pdu(shared_ptr<PDU>, int error);
386 
387  /**
388  * \brief Create a session object connected via unix domain
389  * socket
390  *
391  * This constructor tries to connect to the master agent. If that
392  * fails, the object is created nevertheless and will be in state
393  * disconnected.
394  *
395  * This constructor takes an io_service object as parameter. The
396  * io_service is not destroyed by the constructor.
397  *
398  * \param io_service The io_service object.
399  *
400  * \param description A string describing the subagent. This
401  * description cannot be changed later.
402  *
403  * \param default_timeout The length of time, in seconds, that
404  * the master agent should allow to elapse
405  * before it regards the subagent as not
406  * responding. The value is also used when
407  * waiting synchronously for data from the
408  * master agent (e.g. when registering
409  * stuff). Allowed values are 0-255, with 0
410  * meaning "no default for this session".
411  *
412  * \param ID An Object Identifier that identifies the subagent.
413  * Default is the null OID (no ID).
414  *
415  * \param unix_domain_socket The socket file to connect to.
416  * Defaults to /var/agentx/master, as
417  * desribed in RFC 2741, section 8.2.1
418  * "Well-known Values".
419  */
420  master_proxy(boost::asio::io_service* io_service,
421  std::string description="",
422  uint8_t default_timeout=0,
423  oid ID=oid(),
424  std::string unix_domain_socket="/var/agentx/master");
425 
426  /**
427  * \brief Create a session object connected via unix domain
428  * socket.
429  *
430  * This constructor tries to connect to the master agent. If that
431  * fails, the object is created nevertheless and will be in state
432  * disconnected.
433  *
434  * This constructor creates an io_service object which is destroyed
435  * by the desctructor.
436  *
437  * \param description A string describing the subagent. This
438  * description cannot be changed later.
439  *
440  * \param default_timeout The length of time, in seconds, that
441  * the master agent should allow to elapse
442  * before it regards the subagent as not
443  * responding. The value is also used when
444  * waiting synchronously for data from the
445  * master agent (e.g. when registering
446  * stuff). Allowed values are 0-255, with 0
447  * meaning "no default for this session".
448  *
449  * \param ID An Object Identifier that identifies the subagent.
450  * Default is the null OID (no ID).
451  *
452  * \param unix_domain_socket The socket file to connect to.
453  * Defaults to /var/agentx/master, as
454  * desribed in RFC 2741, section 8.2.1
455  * "Well-known Values".
456  */
457  master_proxy(std::string description="",
458  uint8_t default_timeout=0,
459  oid ID=oid(),
460  std::string unix_domain_socket="/var/agentx/master");
461 
462  /**
463  * \brief Register a subtree with the master agent
464  *
465  * This function registers a subtree (or MIB region).
466  *
467  * \internal
468  *
469  * This method adds the registered subtree to registrations on
470  * success.
471  *
472  * \endinternal
473  *
474  * \param subtree The (root of the) subtree to register.
475  *
476  * \param priority The priority with which to register the subtree.
477  * Default is 127 according to RFC 2741, 6.2.3.
478  * "The agentx-Register-PDU".
479  *
480  * \param timeout The timeout value for the registered subtree, in
481  * seconds. This value overrides the timeout of the
482  * session. Default value is 0 (no override)
483  * according to RFC 2741, 6.2.3. "The
484  * agentx-Register-PDU".
485  *
486  * \exception disconnected If the master_proxy is currently in
487  * state 'disconnected'.
488  *
489  * \exception timeout_exception If the master agent does not
490  * respond within the timeout
491  * interval.
492  *
493  * \exception master_is_unable The master agent was unable to
494  * perform the desired register
495  * request. The reason for that is
496  * unknown.
497  *
498  * \exception duplicate_registration If the exact same subtree was
499  * alread registered, either by
500  * another subagent or by this
501  * subagent.
502  *
503  * \exception master_is_unwilling If the master was unwilling for
504  * some reason to make the desired
505  * registration.
506  *
507  * \exception parse_error A malformed network message was found
508  * during communcation with the master. This
509  * may be a programming error in the master
510  * or in the agentXcpp library. It is
511  * possible that the master actually
512  * performed the desired registration and
513  * that a retry will result in a
514  * duplicate_registration error.
515  *
516  * \note This function invokes run_one() one or more times on the
517  * io_service object.
518  */
519  void register_subtree(oid subtree,
520  uint8_t priority=127,
521  uint8_t timeout=0);
522 
523  /**
524  * \brief Unregister a subtree with the master agent
525  *
526  * This function unregisters a subtree (or MIB region) which has
527  * previously been registered.
528  *
529  * \internal
530  *
531  * This method removes the registered subtree from
532  * registrations.
533  *
534  * \endinternal
535  *
536  * \param subtree The (root of the) subtree to unregister.
537  *
538  * \param priority The priority with which the registration was
539  * done.
540  *
541  * \exception disconnected If the master_proxy is currently in
542  * state 'disconnected'.
543  *
544  * \exception timeout_error If the master agent does not
545  * respond within the timeout interval.
546  *
547  * \exception master_is_unable The master agent was unable to
548  * perform the desired unregister
549  * request. The reason for that is
550  * unknown.
551  *
552  * \exception unknown_registration The MIB region is not currently
553  * registered with this parameters.
554  *
555  * \exception parse_error A malformed network message was found
556  * during communcation with the master. This
557  * may be a programming error in the master
558  * or in the agentXcpp library. It is
559  * possible that the master actually
560  * unregistered the MIB region.
561  *
562  * \note This function invokes run_one() one or more times on the
563  * io_service object.
564  */
565  // TODO: the 'priority' parameter can possibly be omitted: the
566  // value can be stored by master_agent upon subtree registration.
567  void unregister_subtree(oid subtree,
568  uint8_t priority=127);
569 
570  /**
571  * \brief Get the io_service object used by this master_proxy.
572  */
573  boost::asio::io_service* get_io_service() const
574  {
575  return this->io_service;
576  }
577 
578  /**
579  * \brief Check whether the session is in state connected
580  *
581  * \returns true if the session is connected, false otherwise.
582  */
584  {
585  return this->connection->is_connected();
586  }
587 
588  /**
589  * \brief Connect to the master agent.
590  *
591  * \note Upon creation of a session object, the connection is
592  * automatically established. If the current state is
593  * "connected", the function does nothing.
594  *
595  * \exception disconnected If connecting fails.
596  */
597  void connect();
598 
599  /**
600  * \brief Shutdown the session.
601  *
602  * Disconnect from the master agent.
603  *
604  * \note Upon destruction of a session object the session is
605  * automatically shutdown. If the session is in state
606  * "disconnected", this function does nothing.
607  *
608  * \param reason The shutdown reason is reported to the master
609  * agent during shutdown.
610  *
611  * \exception None.
612  */
614 
615  /**
616  * \brief Reconnect to the master agent.
617  *
618  * Disconnects from the master (only if currently connected), then
619  * connects again.
620  *
621  * \exception disconnected If connecting fails.
622  */
623  void reconnect()
624  {
625  this->connection->disconnect();
626  // throws disconnected, which is forwarded:
627  this->connection->connect();
628  }
629 
630  /**
631  * \brief Get the sessionID of the session
632  *
633  * Get the session ID of the last established session, even if the
634  * current state is "disconnected".
635  *
636  * \return The session ID of the last established session. If
637  * object was never connected to the master, 0 is
638  * returned.
639  */
640  uint32_t get_sessionID()
641  {
642  return this->sessionID;
643  }
644 
645  /**
646  * \brief Destructor.
647  *
648  * The destructor cleanly shuts down the session with the reason
649  * 'Shutdown' (if it is currently established) and destroys the
650  * master_proxy object. It also destroys the io_service object if
651  * it was created automatically (i.e. not provided by the user).
652  */
653  ~master_proxy();
654 
655  /**
656  * \brief Add an SNMP variable for serving.
657  *
658  * This adds an SNMP variable which can then be read and/or
659  * written.
660  *
661  * Variables can only be added to MIB regions which were registered
662  * in advance.
663  *
664  * If adding a variable with an id for which another variable is
665  * already registered, it replaces the odl one.
666  *
667  * \param id The OID of the variable.
668  *
669  * \param v The variable.
670  *
671  * \exception unknown_registration If trying to add a variable
672  * with an id which does not reside
673  * within a registered MIB
674  * region.
675  */
676  void add_variable(const oid& id, shared_ptr<variable> v);
677 
678  /**
679  * \brief Remove an SNMP variable so that is not longer accessible.
680  *
681  * This removes a variable previously added using add_variable().
682  * The variable will no longer receive SNMP requests.
683  *
684  * If no variable is known for the given id, nothing happens.
685  *
686  * \param id The OID of the variable to remove. This is the OID
687  * which was given to add_variable().
688  *
689  * \exception None.
690  */
691  void remove_variable(const oid& id);
692  };
693 }
694 
695 #endif