LCOV - code coverage report
Current view: top level - acl - loader.h (source / functions) Hit Total Coverage
Test: report.info Lines: 67 71 94.4 %
Date: 2012-05-15 Functions: 16 30 53.3 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 76 113 67.3 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
       2                 :            : //
       3                 :            : // Permission to use, copy, modify, and/or distribute this software for any
       4                 :            : // purpose with or without fee is hereby granted, provided that the above
       5                 :            : // copyright notice and this permission notice appear in all copies.
       6                 :            : //
       7                 :            : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
       8                 :            : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       9                 :            : // AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
      10                 :            : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
      11                 :            : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
      12                 :            : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
      13                 :            : // PERFORMANCE OF THIS SOFTWARE.
      14                 :            : 
      15                 :            : #ifndef ACL_LOADER_H
      16                 :            : #define ACL_LOADER_H
      17                 :            : 
      18                 :            : #include <exceptions/exceptions.h>
      19                 :            : #include <acl/acl.h>
      20                 :            : #include <cc/data.h>
      21                 :            : #include <boost/function.hpp>
      22                 :            : #include <boost/shared_ptr.hpp>
      23                 :            : #include <map>
      24                 :            : 
      25                 :            : namespace isc {
      26                 :            : namespace acl {
      27                 :            : 
      28                 :            : class AnyOfSpec;
      29                 :            : class AllOfSpec;
      30                 :            : template<typename Mode, typename Context> class LogicOperator;
      31                 :            : 
      32                 :            : /**
      33                 :            :  * \brief Exception for bad ACL specifications.
      34                 :            :  *
      35                 :            :  * This will be thrown by the Loader if the ACL description is malformed
      36                 :            :  * in some way.
      37                 :            :  *
      38                 :            :  * It also can hold optional JSON element where was the error detected, so
      39                 :            :  * it can be examined.
      40                 :            :  *
      41                 :            :  * Checks may subclass this exception for similar errors if they see it fit.
      42                 :            :  */
      43                 :            : class LoaderError : public BadValue {
      44                 :            : private:
      45                 :            :     const data::ConstElementPtr element_;
      46                 :            : public:
      47                 :            :     /**
      48                 :            :      * \brief Constructor.
      49                 :            :      *
      50                 :            :      * Should be used with isc_throw if the fourth argument isn't used.
      51                 :            :      *
      52                 :            :      * \param file The file where the throw happened.
      53                 :            :      * \param line Similar as file, just for the line number.
      54                 :            :      * \param what Human readable description of what happened.
      55                 :            :      * \param element This might be passed to hold the JSON element where
      56                 :            :      *     the error was detected.
      57                 :            :      */
      58                 :         85 :     LoaderError(const char* file, size_t line, const char* what,
      59                 :            :                 data::ConstElementPtr element = data::ConstElementPtr()) :
      60                 :            :         BadValue(file, line, what),
      61                 :        170 :         element_(element)
      62                 :         85 :     {}
      63                 :            : 
      64         [ #  # ]:         85 :     ~ LoaderError() throw() {}
      65                 :            : 
      66                 :            :     /**
      67                 :            :      * \brief Get the element.
      68                 :            :      *
      69                 :            :      * This returns the element where the error was detected. Note that it
      70                 :            :      * might be NULL in some situations.
      71                 :            :      */
      72                 :            :     const data::ConstElementPtr& element() const {
      73                 :            :         return (element_);
      74                 :            :     }
      75                 :            : };
      76                 :            : 
      77                 :            : /**
      78                 :            :  * \brief Loader of the default actions of ACLs.
      79                 :            :  *
      80                 :            :  * Declared outside the Loader class, as this one does not need to be
      81                 :            :  * templated. This will throw LoaderError if the parameter isn't string
      82                 :            :  * or if it doesn't contain one of the accepted values.
      83                 :            :  *
      84                 :            :  * \param action The JSON representation of the action. It must be a string
      85                 :            :  *     and contain one of "ACCEPT", "REJECT" or "DROP.
      86                 :            :  * \note We could define different names or add aliases if needed.
      87                 :            :  */
      88                 :            : BasicAction defaultActionLoader(data::ConstElementPtr action);
      89                 :            : 
      90                 :            : /**
      91                 :            :  * \brief Loader of ACLs.
      92                 :            :  *
      93                 :            :  * The goal of this class is to convert JSON description of an ACL to object
      94                 :            :  * of the ACL class (including the checks inside it).
      95                 :            :  *
      96                 :            :  * The class can be used to load the checks only. This is supposed to be used
      97                 :            :  * by compound checks to create the subexpressions.
      98                 :            :  *
      99                 :            :  * To allow any kind of checks to exist in the application, creators are
     100                 :            :  * registered for the names of the checks.
     101                 :            :  *
     102                 :            :  * An ACL definition looks like this:
     103                 :            :  * \verbatim
     104                 :            :  [
     105                 :            :    {
     106                 :            :       "action": "ACCEPT",
     107                 :            :       "match-type": <parameter>
     108                 :            :    },
     109                 :            :    {
     110                 :            :       "action": "REJECT",
     111                 :            :       "match-type": <parameter>,
     112                 :            :       "another-match-type": [<parameter1>, <parameter2>]
     113                 :            :    },
     114                 :            :    {
     115                 :            :       "action": "DROP"
     116                 :            :    }
     117                 :            :  ]
     118                 :            :  \endverbatim
     119                 :            :  *
     120                 :            :  * This is a list of elements. Each element must have an "action"
     121                 :            :  * entry/keyword. That one specifies which action is returned if this
     122                 :            :  * element matches (the value of the key is passed to the action loader
     123                 :            :  * (see the constructor). It may be any piece of JSON which the action
     124                 :            :  * loader expects.
     125                 :            :  *
     126                 :            :  * The rest of the element are matches. The left side is the name of the
     127                 :            :  * match type (for example match for source IP address or match for message
     128                 :            :  * size). The parameter is whatever is needed to describe the match and
     129                 :            :  * depends on the match type, the loader passes it verbatim to creator
     130                 :            :  * of that match type.
     131                 :            :  *
     132                 :            :  * There may be multiple match types in single element. In such case, all
     133                 :            :  * of the matches must match for the element to take action (so, in the second
     134                 :            :  * element, both "match-type" and "another-match-type" must be satisfied).
     135                 :            :  * If there's no match in the element, the action is taken/returned without
     136                 :            :  * conditions, every time (makes sense as the last entry, as the ACL will
     137                 :            :  * never get past it).
     138                 :            :  *
     139                 :            :  * The second entry shows another thing - if there's a list as the value
     140                 :            :  * for some match and the match itself is not expecting a list, it is taken
     141                 :            :  * as an "or" - a match for at last one of the choices in the list must match.
     142                 :            :  * So, for the second entry, both "match-type" and "another-match-type" must
     143                 :            :  * be satisfied, but the another one is satisfied by either parameter1 or
     144                 :            :  * parameter2.
     145                 :            :  */
     146         [ +  - ]:         64 : template<typename Context, typename Action = BasicAction> class Loader {
     147                 :            : public:
     148                 :            :     /**
     149                 :            :      * \brief Constructor.
     150                 :            :      *
     151                 :            :      * \param defaultAction The default action for created ACLs.
     152                 :            :      * \param actionLoader is the loader which will be used to convert actions
     153                 :            :      *     from their JSON representation. The default value is suitable for
     154                 :            :      *     the BasicAction enum. If you did not specify the second
     155                 :            :      *     template argument, you don't need to specify this loader.
     156                 :            :      */
     157                 :          0 :     Loader(const Action& defaultAction,
     158                 :            :            const boost::function1<Action, data::ConstElementPtr>
     159                 :            :                &actionLoader = &defaultActionLoader) :
     160                 :            :         default_action_(defaultAction),
     161                 :         72 :         action_loader_(actionLoader)
     162                 :          0 :     {}
     163                 :            : 
     164                 :            :     /**
     165                 :            :      * \brief Creator of the checks.
     166                 :            :      *
     167                 :            :      * This can be registered within the Loader and will be used to create the
     168                 :            :      * checks. It is expected multiple creators (for multiple types, one can
     169                 :            :      * handle even multiple names) will be created and registered to support
     170                 :            :      * range of things we could check. This allows for customizing/extending
     171                 :            :      * the loader.
     172                 :            :      */
     173                 :        123 :     class CheckCreator {
     174                 :            :     public:
     175                 :            :         /** \brief Virtual class needs virtual destructor */
     176                 :        107 :         virtual ~CheckCreator() {}
     177                 :            : 
     178                 :            :         /**
     179                 :            :          * \brief List of names supported by this loader.
     180                 :            :          *
     181                 :            :          * List of all names for which this loader is able to create the
     182                 :            :          * checks. There can be multiple names, to support both aliases
     183                 :            :          * to the same checks and creators capable of creating multiple
     184                 :            :          * types of checks.
     185                 :            :          */
     186                 :            :         virtual std::vector<std::string> names() const = 0;
     187                 :            : 
     188                 :            :         /**
     189                 :            :          * \brief Creates the check.
     190                 :            :          *
     191                 :            :          * This function does the actual creation. It is passed all the
     192                 :            :          * relevant data and is supposed to return shared pointer to the
     193                 :            :          * check.
     194                 :            :          *
     195                 :            :          * It is expected to throw the LoaderError exception when the
     196                 :            :          * definition is invalid.
     197                 :            :          *
     198                 :            :          * \param name The type name of the check. If the creator creates
     199                 :            :          *     only one type of check, it can safely ignore this parameter.
     200                 :            :          * \param definition The part of JSON describing the parameters of
     201                 :            :          *     check. As there's no way for the loader to know how the
     202                 :            :          *     parameters might look like, they are not checked in any way.
     203                 :            :          *     Therefore it's up to the creator (or the check being created)
     204                 :            :          *     to validate the data and throw if it is bad.
     205                 :            :          * \param loader Current loader calling this creator. This can be used
     206                 :            :          *     to load subexpressions in case of compound check.
     207                 :            :          */
     208                 :            :         virtual boost::shared_ptr<Check<Context> > create(
     209                 :            :             const std::string& name, data::ConstElementPtr definition,
     210                 :            :             const Loader<Context, Action>& loader) = 0;
     211                 :            : 
     212                 :            :         /**
     213                 :            :          * \brief Is list or-abbreviation allowed?
     214                 :            :          *
     215                 :            :          * If this returns true and the parameter (eg. the value we check
     216                 :            :          * against, the one that is passed as the second parameter of create)
     217                 :            :          * is list, the loader will call the create method with each element of
     218                 :            :          * the list and aggregate all the results in OR compound check. If it
     219                 :            :          * is false, the parameter is passed verbatim no matter if it is or
     220                 :            :          * isn't a list. For example, IP check will have this as true (so
     221                 :            :          * multiple IP addresses can be passed as options), but AND operator
     222                 :            :          * will return false and handle the list of subexpressions itself.
     223                 :            :          *
     224                 :            :          * The rationale behind this is that it is common to specify list of
     225                 :            :          * something that matches (eg. list of IP addresses).
     226                 :            :          */
     227                 :          5 :         virtual bool allowListAbbreviation() const {
     228                 :          5 :             return (true);
     229                 :            :         }
     230                 :            :     };
     231                 :            : 
     232                 :            :     /**
     233                 :            :      * \brief Register another check creator.
     234                 :            :      *
     235                 :            :      * Adds a creator to the list of known ones. The creator's list of names
     236                 :            :      * must be disjoint with the names already known to the creator or the
     237                 :            :      * LoaderError exception is thrown. In such case, the creator is not
     238                 :            :      * registered under any of the names. In case of other exceptions, like
     239                 :            :      * bad_alloc, only weak exception safety is guaranteed.
     240                 :            :      *
     241                 :            :      * \param creator Shared pointer to the creator.
     242                 :            :      * \note We don't support deregistration yet, but it is expected it will
     243                 :            :      *     be needed in future, when we have some kind of plugins. These
     244                 :            :      *     plugins might want to unload, in which case they would need to
     245                 :            :      *     deregister their creators. It is expected they would pass the same
     246                 :            :      *     pointer to such method as they pass here.
     247                 :            :      */
     248                 :        114 :     void registerCreator(boost::shared_ptr<CheckCreator> creator) {
     249                 :            :         // First check we can insert all the names
     250                 :            :         typedef std::vector<std::string> Strings;
     251                 :        228 :         const Strings names(creator->names());
     252         [ +  + ]:        222 :         for (Strings::const_iterator i(names.begin()); i != names.end();
     253                 :            :              ++i) {
     254         [ +  + ]:        120 :             if (creators_.find(*i) != creators_.end()) {
     255 [ +  - ][ +  - ]:         36 :                 isc_throw(LoaderError, "The loader already contains creator "
         [ +  - ][ +  - ]
     256                 :            :                           "named " << *i);
     257                 :            :             }
     258                 :            :         }
     259                 :            :         // Now insert them
     260         [ +  + ]:        209 :         for (Strings::const_iterator i(names.begin()); i != names.end();
     261                 :            :              ++i) {
     262         [ +  - ]:        107 :             creators_[*i] = creator;
     263                 :            :         }
     264                 :        102 :     }
     265                 :            : 
     266                 :            :     /**
     267                 :            :      * \brief Load a check.
     268                 :            :      *
     269                 :            :      * This parses a check dict (block, the one element of ACL) and calls a
     270                 :            :      * creator (or creators, if more than one check is found inside) for it. It
     271                 :            :      * ignores the "action" key, as it is a reserved keyword used to specify
     272                 :            :      * actions inside the ACL.
     273                 :            :      *
     274                 :            :      * This may throw LoaderError if it is not a dict or if some of the type
     275                 :            :      * names is not known (there's no creator registered for it). The
     276                 :            :      * exceptions from creators aren't caught.
     277                 :            :      *
     278                 :            :      * \param description The JSON description of the check.
     279                 :            :      */
     280                 :         80 :     boost::shared_ptr<Check<Context> > loadCheck(const data::ConstElementPtr&
     281                 :            :                                                  description) const
     282                 :            :     {
     283                 :            :         // Get the description as a map
     284                 :            :         typedef std::map<std::string, data::ConstElementPtr> Map;
     285                 :         80 :         Map map;
     286                 :            :         try {
     287         [ +  + ]:         80 :             map = description->mapValue();
     288                 :            :         }
     289         [ -  + ]:         20 :         catch (const data::TypeError&) {
     290 [ -  + ][ -  + ]:         30 :             isc_throw_1(LoaderError, "Check description is not a map",
                 [ -  + ]
     291                 :            :                         description);
     292                 :            :         }
     293                 :            :         // Call the internal part with extracted map
     294         [ +  + ]:        115 :         return (loadCheck(description, map));
     295                 :            :     }
     296                 :            : 
     297                 :            :     /**
     298                 :            :      * \brief Load an ACL.
     299                 :            :      *
     300                 :            :      * This parses an ACL list, creates the checks and actions of each element
     301                 :            :      * and returns it.
     302                 :            :      *
     303                 :            :      * No exceptions from \c loadCheck (therefore from whatever creator is
     304                 :            :      * used) and from the actionLoader passed to constructor are caught.
     305                 :            :      *
     306                 :            :      * \exception InvalidParameter The given element is NULL (most likely a
     307                 :            :      * caller's bug)
     308                 :            :      * \exception LoaderError The given element isn't a list or the
     309                 :            :      * "action" key is missing in some element
     310                 :            :      *
     311                 :            :      * \param description The JSON list of ACL.
     312                 :            :      *
     313                 :            :      * \return The newly created ACL object
     314                 :            :      */
     315                 :        254 :     boost::shared_ptr<ACL<Context, Action> > load(const data::ConstElementPtr&
     316                 :            :                                                   description) const
     317                 :            :     {
     318         [ +  + ]:        254 :         if (!description) {
     319         [ +  - ]:          2 :             isc_throw(isc::InvalidParameter,
     320                 :            :                       "Null description is passed to ACL loader");
     321                 :            :         }
     322                 :            : 
     323                 :            :         // We first check it's a list, so we can use the list reference
     324                 :            :         // (the list may be huge)
     325         [ +  + ]:        253 :         if (description->getType() != data::Element::list) {
     326 [ +  - ][ +  - ]:         45 :             isc_throw_1(LoaderError, "ACL not a list", description);
     327                 :            :         }
     328                 :            :         // First create an empty ACL
     329                 :        238 :         const List &list(description->listValue());
     330                 :            :         boost::shared_ptr<ACL<Context, Action> > result(
     331                 :        238 :             new ACL<Context, Action>(default_action_));
     332                 :            :         // Run trough the list of elements
     333         [ +  + ]:        401 :         for (List::const_iterator i(list.begin()); i != list.end(); ++i) {
     334         [ +  - ]:        226 :             Map map;
     335                 :            :             try {
     336         [ +  + ]:        226 :                 map = (*i)->mapValue();
     337                 :            :             }
     338         [ -  + ]:         32 :             catch (const data::TypeError&) {
     339 [ -  + ][ -  + ]:         48 :                 isc_throw_1(LoaderError, "ACL element not a map", *i);
                 [ -  + ]
     340                 :            :             }
     341                 :            :             // Create an action for the element
     342         [ +  - ]:        420 :             const Map::const_iterator action(map.find("action"));
     343         [ +  + ]:        210 :             if (action == map.end()) {
     344 [ +  - ][ +  - ]:         15 :                 isc_throw_1(LoaderError, "No action in ACL element", *i);
                 [ +  - ]
     345                 :            :             }
     346         [ +  + ]:        205 :             const Action acValue(action_loader_(action->second));
     347                 :            :             // Now create the check if there's one
     348         [ +  + ]:        202 :             if (map.size() >= 2) { // One is the action, another one the check
     349         [ +  + ]:        227 :                 result->append(loadCheck(*i, map), acValue);
              [ +  -  + ]
     350                 :            :             } else {
     351                 :            :                 // In case there's no check, this matches every time. We
     352                 :            :                 // simulate it by our own private "True" check.
     353         [ +  - ]:        138 :                 result->append(boost::shared_ptr<Check<Context> >(new True()),
              [ +  -  + ]
     354                 :            :                                acValue);
     355                 :            :             }
     356                 :            :         }
     357                 :        175 :         return (result);
     358                 :            :     }
     359                 :            : 
     360                 :            : private:
     361                 :            :     // Some type aliases to save typing
     362                 :            :     typedef std::map<std::string, boost::shared_ptr<CheckCreator> > Creators;
     363                 :            :     typedef std::map<std::string, data::ConstElementPtr> Map;
     364                 :            :     typedef std::vector<data::ConstElementPtr> List;
     365                 :            :     // Private members
     366                 :            :     Creators creators_;
     367                 :            :     const Action default_action_;
     368                 :            :     const boost::function1<Action, data::ConstElementPtr> action_loader_;
     369                 :            : 
     370                 :            :     /**
     371                 :            :      * \brief Internal version of loadCheck.
     372                 :            :      *
     373                 :            :      * This is the internal part, shared between load and loadCheck.
     374                 :            :      * \param description The bit of JSON (used in exceptions).
     375                 :            :      * \param map The extracted map describing the check. It does change
     376                 :            :      *     the map.
     377                 :            :      */
     378                 :        207 :     boost::shared_ptr<Check<Context> > loadCheck(const data::ConstElementPtr&
     379                 :            :                                                  description, Map& map) const
     380                 :            :     {
     381                 :            :         // Remove the action keyword
     382                 :        414 :         map.erase("action");
     383                 :            :         // Now, do we have any definition? Or is it and abbreviation?
     384      [ -  +  - ]:        140 :         switch (map.size()) {
     385                 :            :             case 0:
     386 [ +  - ][ +  - ]:          3 :                 isc_throw_1(LoaderError, "Check description is empty",
     387                 :            :                             description);
     388                 :            :             case 1: {
     389                 :            :                 // Get the first and only item
     390                 :        204 :                 const Map::const_iterator checkDesc(map.begin());
     391                 :        204 :                 const std::string& name(checkDesc->first);
     392                 :            :                 const typename Creators::const_iterator
     393                 :        408 :                     creatorIt(creators_.find(name));
     394         [ +  + ]:        204 :                 if (creatorIt == creators_.end()) {
     395 [ +  - ][ +  - ]:         15 :                     isc_throw_1(LoaderError, "No creator for ACL check " <<
                 [ +  - ]
     396                 :            :                                 name, description);
     397                 :            :                 }
     398 [ +  + ][ +  + ]:        199 :                 if (creatorIt->second->allowListAbbreviation() &&
                 [ +  + ]
     399                 :            :                     checkDesc->second->getType() == data::Element::list) {
     400                 :            :                     // Or-abbreviated form - create an OR and put everything
     401                 :            :                     // inside.
     402                 :            :                     const std::vector<data::ConstElementPtr>&
     403                 :          2 :                         params(checkDesc->second->listValue());
     404                 :            :                     boost::shared_ptr<LogicOperator<AnyOfSpec, Context> >
     405                 :          2 :                         oper(new LogicOperator<AnyOfSpec, Context>);
     406         [ +  + ]:          6 :                     for (std::vector<data::ConstElementPtr>::const_iterator
     407                 :          2 :                              i(params.begin());
     408                 :            :                          i != params.end(); ++i) {
     409         [ +  - ]:          8 :                         oper->addSubexpression(
     410                 :            :                             creatorIt->second->create(name, *i, *this));
     411                 :            :                     }
     412                 :            :                     return (oper);
     413                 :            :                 }
     414                 :            :                 // Create the check and return it
     415                 :            :                 return (creatorIt->second->create(name, checkDesc->second,
     416         [ +  + ]:        394 :                                                   *this));
     417                 :            :             }
     418                 :            :             default: {
     419                 :            :                 // This is the AND-abbreviated form. We need to create an
     420                 :            :                 // AND (or "ALL") operator, loop trough the whole map and
     421                 :            :                 // fill it in. We do a small trick - we create bunch of
     422                 :            :                 // single-item maps, call this loader recursively (therefore
     423                 :            :                 // it will get into the "case 1" branch, where there is
     424                 :            :                 // the actual loading) and use the results to fill the map.
     425                 :            :                 //
     426                 :            :                 // We keep the description the same, there's nothing we could
     427                 :            :                 // take out (we could create a new one, but that would be
     428                 :            :                 // confusing, as it is used for error messages only).
     429                 :            :                 boost::shared_ptr<LogicOperator<AllOfSpec, Context> >
     430                 :          2 :                     oper(new LogicOperator<AllOfSpec, Context>);
     431         [ +  + ]:          6 :                 for (Map::const_iterator i(map.begin()); i != map.end(); ++i) {
     432         [ +  - ]:          4 :                     Map singleSubexpr;
     433                 :          4 :                     singleSubexpr.insert(*i);
     434         [ +  - ]:          4 :                     oper->addSubexpression(loadCheck(description,
     435                 :            :                                                      singleSubexpr));
     436                 :            :                 }
     437                 :            :                 return (oper);
     438                 :            :             }
     439                 :            :         }
     440                 :            :     }
     441                 :            : 
     442                 :            :     /**
     443                 :            :      * \brief Check that always matches.
     444                 :            :      *
     445                 :            :      * This one is used internally for ACL elements without condition. We may
     446                 :            :      * want to make this publicly accesible sometime maybe, but for now,
     447                 :            :      * there's no need.
     448                 :            :      */
     449                 :         69 :     class True : public Check<Context> {
     450                 :            :     public:
     451                 :         33 :         virtual bool matches(const Context&) const { return (true); };
     452                 :          0 :         virtual unsigned cost() const { return (1); }
     453                 :            :         // We don't write "true" here, as this one was created using empty
     454                 :            :         // input
     455                 :          0 :         virtual std::string toText() const { return ""; }
     456                 :            :     };
     457                 :            : };
     458                 :            : 
     459                 :            : }
     460                 :            : }
     461                 :            : 
     462                 :            : /*
     463                 :            :  * This include at the end of the file is unusual. But we need to include it,
     464                 :            :  * we use template classes from there. However, they need to be present only
     465                 :            :  * at instantiation of our class, which will happen below this header.
     466                 :            :  *
     467                 :            :  * The problem is, the header uses us as well, therefore there's a circular
     468                 :            :  * dependency. If we loaded it at the beginning and someone loaded us first,
     469                 :            :  * the logic_check header wouldn't have our definitions. This way, no matter
     470                 :            :  * in which order they are loaded, the definitions from this header will be
     471                 :            :  * above the ones from logic_check.
     472                 :            :  */
     473                 :            : #include "logic_check.h"
     474                 :            : 
     475                 :            : #endif
     476                 :            : 
     477                 :            : // Local Variables:
     478                 :            : // mode: c++
     479                 :            : // End:

Generated by: LCOV version 1.9