LCOV - code coverage report
Current view: top level - snapwebsites - snap_config.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 5 216 2.3 %
Date: 2019-12-15 17:13:15 Functions: 2 32 6.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Snap Websites Server -- configuration reader
       2             : // Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
       3             : //
       4             : // This program is free software; you can redistribute it and/or modify
       5             : // it under the terms of the GNU General Public License as published by
       6             : // the Free Software Foundation; either version 2 of the License, or
       7             : // (at your option) any later version.
       8             : //
       9             : // This program is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             : // GNU General Public License for more details.
      13             : //
      14             : // You should have received a copy of the GNU General Public License
      15             : // along with this program; if not, write to the Free Software
      16             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      17             : 
      18             : // self
      19             : //
      20             : #include "snapwebsites/snap_config.h"
      21             : 
      22             : // snapwebsites lib
      23             : //
      24             : #include "snapwebsites/log.h"
      25             : #include "snapwebsites/qlockfile.h"
      26             : #include "snapwebsites/snap_thread.h"
      27             : 
      28             : 
      29             : // snapdev lib
      30             : //
      31             : #include <snapdev/not_reached.h>
      32             : 
      33             : 
      34             : // Qt lib
      35             : //
      36             : #include <QDateTime>
      37             : 
      38             : 
      39             : // boost lib
      40             : //
      41             : #include <boost/algorithm/string.hpp>
      42             : 
      43             : 
      44             : // C++ lib
      45             : //
      46             : #include <memory>
      47             : #include <sstream>
      48             : 
      49             : 
      50             : // C lib
      51             : //
      52             : #include <syslog.h>
      53             : #include <unistd.h>
      54             : 
      55             : 
      56             : // included last
      57             : //
      58             : #include "snapdev/poison.h"
      59             : 
      60             : 
      61             : 
      62             : namespace snap
      63             : {
      64             : 
      65             : namespace
      66             : {
      67             : 
      68             : 
      69             : /** \brief All the configurations are saved in one object.
      70             :  *
      71             :  * At this point we decided that there was no need for us to support
      72             :  * dynamic configurations, i.e. configurations that you can allocate,
      73             :  * load, tweak/use, then drop. The only reason why you'd want to
      74             :  * re-allocate a configuration would be to satisfy a RELOADCONFIG
      75             :  * event which we do not yet support (properly) in most cases
      76             :  * because we copy the configuration information in various places
      77             :  * (and at times these are used to do things like connect to another
      78             :  * server...)
      79             :  *
      80             :  * So at this point we do not allow such dynamism. Even if we were,
      81             :  * we would want you to make use of this interface instead.
      82             :  */
      83           2 : std::shared_ptr<snap_configurations>        g_configurations;
      84             : 
      85             : 
      86             : /** \brief Mutex used to make the configuration thread safe.
      87             :  *
      88             :  * This object is used whenever the configuration is accessed in
      89             :  * order to make it thread safe.
      90             :  */
      91           2 : std::shared_ptr<snap_thread::snap_mutex>    g_mutex;
      92             : 
      93             : 
      94             : /** \brief The path to the configuration files.
      95             :  *
      96             :  * This variable holds the path to the various configuration files.
      97             :  * The default is "/etc/snapwebsites". Most daemon will offer you
      98             :  * a way to change that value with a "--config" command line option.
      99             :  *
     100             :  * Once one configuration file was read, that parameter becomes
     101             :  * immutable.
     102             :  */
     103           2 : std::string                                 g_configurations_path = "/etc/snapwebsites";
     104             : 
     105             : 
     106             : /** \brief true once we started reading files.
     107             :  *
     108             :  * The parameter goes from false to true once we read the very first
     109             :  * configuration file. This allows us to prevent changing the path
     110             :  * to the configuration data past that call.
     111             :  *
     112             :  * The default is false.
     113             :  */
     114             : bool                                        g_configuration_has_started = false;
     115             : 
     116             : 
     117             : 
     118           0 : class snap_config_file
     119             : {
     120             : public:
     121             :     typedef std::shared_ptr<snap_config_file>       pointer_t;
     122             :     typedef std::map<std::string, pointer_t>        map_t;
     123             : 
     124             :                         snap_config_file(std::string const & configuration_filename, std::string const & override_filename);
     125             : 
     126             :     std::string const & get_configuration_filename() const;
     127             :     std::string const & get_override_filename() const;
     128             : 
     129             :     bool                exists();
     130             : 
     131             :     //void                clear();
     132             :     void                read_config_file();
     133             :     bool                write_config_file(bool override_file);
     134             : 
     135             :     std::string         get_parameter(std::string const & parameter_name) const;
     136             :     bool                has_parameter(std::string const & parameter_name) const;
     137             :     void                set_parameter(std::string const & parameter_name, std::string const & value);
     138             :     snap_configurations::parameter_map_t const &
     139             :                         get_parameters() const;
     140             :     void                set_parameters(snap_configurations::parameter_map_t const & params);
     141             : 
     142             : private:
     143             :     bool                actual_read_config_file(std::string const & filename, bool quiet);
     144             :     bool                actual_write_config_file(std::string const & filename);
     145             : 
     146             :     std::string const                       f_configuration_filename;
     147             :     std::string const                       f_override_filename;
     148             :     snap_configurations::parameter_map_t    f_parameters = snap_configurations::parameter_map_t();
     149             :     bool                                    f_exists = false;
     150             : };
     151             : 
     152             : 
     153             : 
     154             : /** \brief A map of configurations.
     155             :  *
     156             :  * Most of our systems load configuration files with a "hard coded" filename
     157             :  * which can be accessed from many different locations. That 
     158             :  */
     159           2 : snap_config_file::map_t      g_config_files;
     160             : 
     161             : 
     162             : 
     163             : /** \brief The configuration file.
     164             :  *
     165             :  * The constructor saves the filename of the configuration file.
     166             :  * The filename cannot be modified later.
     167             :  *
     168             :  * \param[in] configuration_filename  The filename for this configuration file.
     169             :  * \param[in] override_filename  The name of the override, that file is
     170             :  *            checked first and if a field exists in it, that value is used.
     171             :  */
     172           0 : snap_config_file::snap_config_file(std::string const & configuration_filename, std::string const & override_filename)
     173             :     : f_configuration_filename(configuration_filename)
     174           0 :     , f_override_filename(override_filename)
     175             : {
     176             :     // empty
     177           0 : }
     178             : 
     179             : 
     180             : /** \brief Get the filename of this configuration file.
     181             :  *
     182             :  * This function gets the filename of this configuration file as was
     183             :  * defined on the constructor.
     184             :  *
     185             :  * \return The filename of this configuration file.
     186             :  */
     187           0 : std::string const & snap_config_file::get_configuration_filename() const
     188             : {
     189           0 :     return f_configuration_filename;
     190             : }
     191             : 
     192             : 
     193             : /** \brief Get the override_filename of this configuration file.
     194             :  *
     195             :  * This function gets the override_filename of this configuration file as was
     196             :  * defined on the constructor.
     197             :  *
     198             :  * \return The override_filename of this configuration file.
     199             :  */
     200           0 : std::string const & snap_config_file::get_override_filename() const
     201             : {
     202           0 :     return f_override_filename;
     203             : }
     204             : 
     205             : 
     206             : //void snap_config_file::clear()
     207             : //{
     208             : //    f_parameters.clear();
     209             : //}
     210             : 
     211             : 
     212             : /** \brief Return the value of the f_exists flag.
     213             :  *
     214             :  * This function lets you know whether the file exists or not. By default
     215             :  * it is set to false until the read_config_file() gets called. It may
     216             :  * remain set to false if the file is not found at that time.
     217             :  *
     218             :  * \return true if configuration file exists, false otherwise
     219             :  *
     220             :  * \sa read_config_file()
     221             :  */
     222           0 : bool snap_config_file::exists()
     223             : {
     224           0 :     return f_exists;
     225             : }
     226             : 
     227             : 
     228             : /** \brief Read the configuration file into memory.
     229             :  *
     230             :  * This function reads the configuration file from disk to memory.
     231             :  * It will stay there until the process leaves.
     232             :  *
     233             :  * The file is searched in the specified configuration path
     234             :  * and under a sub-directory of that configuration path named
     235             :  * "snapwebsites.d".
     236             :  *
     237             :  * \code
     238             :  *      <configuration path>/<configuration filename>
     239             :  *      <configuration path>/snapwebsites.d/<configuration filename>
     240             :  * \endcode
     241             :  *
     242             :  * This allows you to NOT modify the original .conf files, and instead
     243             :  * edit a version where you define just the few fields you want to
     244             :  * modify within the "snapwebsites.d" sub-directory.
     245             :  *
     246             :  * \note
     247             :  * Sets the f_exists flag.
     248             :  *
     249             :  * \sa actual_read_config_file()
     250             :  * \sa exists()
     251             :  */
     252           0 : void snap_config_file::read_config_file()
     253             : {
     254             :     // first use of the g_configurations_path variable
     255             :     // now the set_configuration_path() function cannot be called.
     256             :     //
     257           0 :     g_configuration_has_started = true;
     258             : 
     259             :     // if the filename includes any "." or "/", it is not one of our
     260             :     // files so we instead load the file as is
     261             :     //
     262           0 :     std::string::size_type const pos(f_configuration_filename.find_first_of("./"));
     263           0 :     if(pos != std::string::npos)
     264             :     {
     265             :         // we use 'true' (i.e. "keep quiet") here because in some cases
     266             :         // it is normal that the file does not exist
     267             :         //
     268           0 :         f_exists = actual_read_config_file(f_configuration_filename, true);
     269             : 
     270             :         // give a chance to other configuration files to have one
     271             :         // override
     272             :         //
     273             :         // TODO: later we want to support any number with an "'*' + sort"
     274             :         //       (like apache2 and other daemons do)
     275             :         //
     276           0 :         if(f_exists && !f_override_filename.empty())
     277             :         {
     278           0 :             actual_read_config_file(f_override_filename, true);
     279             :         }
     280             :     }
     281             :     else
     282             :     {
     283           0 :         f_exists = actual_read_config_file(g_configurations_path + "/" + f_configuration_filename + ".conf", false);
     284             : 
     285             :         // second try reading a file of the same name in a sub-directory named
     286             :         // "snapwebsites.d"; we have to do it last because we do overwrite
     287             :         // parameters (i.e. we keep the very last instance of each parameter
     288             :         // read from files.)
     289             :         //
     290           0 :         if(f_exists)
     291             :         {
     292             :             // TODO: allow for a different sub-directory name to be more
     293             :             //       versatiled
     294             :             //
     295           0 :             actual_read_config_file(g_configurations_path + "/snapwebsites.d/" + f_configuration_filename + ".conf", true);
     296             :         }
     297             :     }
     298           0 : }
     299             : 
     300             : 
     301             : /** \brief Read the configuration file itself.
     302             :  *
     303             :  * This is the function that actually reads the file. We use a sub-function
     304             :  * so that way we can read files in a sub-directory, such as "snapwebsites.d,"
     305             :  * with user modifications.
     306             :  *
     307             :  * This function attempts to read one file. The name of the sub-directory
     308             :  * is determined by the caller.
     309             :  *
     310             :  * \exception snap_configurations_exception_config_error
     311             :  * If there is an error reading the file or a parsing error, this exception
     312             :  * is raised.
     313             :  *
     314             :  * \param[in] filename  The name of the file to read from.
     315             :  * \param[in] quiet     Whether to keep quiet about missing files (do not
     316             :  *                      raise exception, just return false).
     317             :  *
     318             :  * \return true if read, false on failure to read file.
     319             :  *
     320             :  * \sa read_config_file()
     321             :  */
     322           0 : bool snap_config_file::actual_read_config_file(std::string const & filename, bool quiet)
     323             : {
     324             :     // read the configuration file now
     325           0 :     QFile c(QString::fromUtf8(filename.c_str()));
     326             :     //
     327           0 :     if( !c.exists() && quiet )
     328             :     {
     329           0 :         return false;
     330             :     }
     331             :     //
     332           0 :     if(!c.open(QIODevice::ReadOnly))
     333             :     {
     334             :         // if for nothing else we need to have the list of plugins so we always
     335             :         // expect to have a configuration file... if we're here we could not
     336             :         // read it, unfortunately
     337           0 :         std::stringstream ss;
     338           0 :         ss << "cannot read configuration file \"" << filename << "\"";
     339           0 :         SNAP_LOG_WARNING(ss.str())(".");
     340           0 :         syslog( LOG_CRIT, "%s, server not started. (in server::config())", ss.str().c_str() );
     341             :         //throw snap_configurations_exception_config_error(ss.str());
     342           0 :         return false;
     343             :     }
     344             : 
     345             :     // read the configuration file variables as parameters
     346             :     //
     347           0 :     std::string prefix;
     348             :     char buf[1024];
     349           0 :     for(int line(1); c.readLine(buf, sizeof(buf)) > 0; ++line)
     350             :     {
     351             :         // make sure the last byte is '\0'
     352           0 :         buf[sizeof(buf) - 1] = '\0';
     353           0 :         int len(static_cast<int>(strlen(buf)));
     354           0 :         if(len == 0 || (buf[len - 1] != '\n' && buf[len - 1] != '\r'))
     355             :         {
     356           0 :             std::stringstream ss;
     357           0 :             ss << "line " << line << " in \"" << filename << "\" is too long";
     358           0 :             SNAP_LOG_FATAL(ss.str())(".");
     359           0 :             syslog( LOG_CRIT, "%s, server not started. (in server::config())", ss.str().c_str() );
     360           0 :             throw snap_configurations_exception_config_error(ss.str());
     361             :         }
     362           0 :         buf[len - 1] = '\0';
     363           0 :         --len;
     364           0 :         while(len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
     365             :         {
     366           0 :             --len;
     367           0 :             buf[len] = '\0';
     368             :         }
     369           0 :         if(len == 0)
     370             :         {
     371             :             // empty line
     372           0 :             continue;
     373             :         }
     374           0 :         char * n(buf);
     375           0 :         while(isspace(*n))
     376             :         {
     377           0 :             ++n;
     378             :         }
     379           0 :         if(*n == '#' || *n == ';' || *n == '\0')
     380             :         {
     381             :             // comment or empty line
     382           0 :             continue;
     383             :         }
     384           0 :         if(*n == '[')
     385             :         {
     386             :             // support for INI files starts here, we take the name between
     387             :             // [ and ] and save it as a "prefix" to our follow list of
     388             :             // names until another section appears
     389             :             //
     390           0 :             do
     391             :             {
     392           0 :                 ++n;
     393             :             }
     394           0 :             while(isspace(*n));
     395           0 :             char * v(n);
     396           0 :             while(*v != ']' && *v != '\0' && !isspace(*v) && *v != ':')
     397             :             {
     398           0 :                 ++v;
     399             :             }
     400           0 :             char * e(v);
     401           0 :             while(isspace(*v))
     402             :             {
     403           0 :                 ++v;
     404             :             }
     405             :             // Note: we do not support "[]" to reset back to "global"
     406             :             //       variables; just place your global variables first
     407             :             //
     408           0 :             if(*v != ']' || n == e)
     409             :             {
     410           0 :                 std::stringstream ss;
     411           0 :                 ss << "invalid section on line " << line << " in \"" << filename << "\", no equal sign found";
     412           0 :                 SNAP_LOG_FATAL(ss.str())(".");
     413           0 :                 syslog( LOG_CRIT, "%s, server not started. (in server::config())", ss.str().c_str() );
     414           0 :                 throw snap_configurations_exception_config_error(ss.str());
     415             :             }
     416             :             // right away add the "::" to the prefix so we can use it as is
     417             :             // when we find a variable
     418             :             //
     419           0 :             prefix = std::string(n, e - n) + "::";
     420             :         }
     421             :         else
     422             :         {
     423           0 :             char * v(n);
     424           0 :             while(*v != '=' && *v != '\0')
     425             :             {
     426             :                 // TODO verify that the name is only ASCII? (probably not too
     427             :                 //      important because if not it will be ignored anyway)
     428             :                 //      Note that the layout expects names including colons (:)
     429             :                 //      as a namespace separator: layout::layout, layout::theme.
     430           0 :                 ++v;
     431             :             }
     432           0 :             if(*v != '=')
     433             :             {
     434           0 :                 std::stringstream ss;
     435           0 :                 ss << "invalid variable on line " << line << " in \"" << filename << "\", no equal sign found";
     436           0 :                 SNAP_LOG_FATAL(ss.str())(".");
     437           0 :                 syslog( LOG_CRIT, "%s, server not started. (in server::config())", ss.str().c_str() );
     438           0 :                 throw snap_configurations_exception_config_error(ss.str());
     439             :             }
     440             :             char * e;
     441           0 :             for(e = v; e > n && isspace(e[-1]); --e);
     442           0 :             *e = '\0';
     443           0 :             do
     444             :             {
     445           0 :                 ++v;
     446             :             }
     447           0 :             while(isspace(*v));
     448           0 :             for(e = v + strlen(v); e > v && isspace(e[-1]); --e);
     449           0 :             *e = '\0';
     450           0 :             if(v != e && ((v[0] == '\'' && e[-1] == '\'') || (v[0] == '"' && e[-1] == '"')))
     451             :             {
     452             :                 // remove single or double quotes
     453             :                 //
     454           0 :                 v++;
     455           0 :                 e[-1] = '\0';
     456             :             }
     457             : 
     458             :             // restore the escaped newlines if any
     459             :             // right now this is the only thing we escape, the rest can
     460             :             // stay as it is and still works
     461             :             //
     462           0 :             std::string value(v);
     463           0 :             boost::algorithm::replace_all(value, "\\n", "\n");
     464             : 
     465             :             // keep the last read value in that section
     466             :             //
     467           0 :             f_parameters[prefix + n] = value;
     468             :         }
     469             :     }
     470             : 
     471           0 :     return true;
     472             : }
     473             : 
     474             : 
     475             : /** \brief Write the data back to the configuration file.
     476             :  *
     477             :  * This function writes the existing data back to the configuration file.
     478             :  *
     479             :  * This function is somewhat dangerous in the sense that it destroys all
     480             :  * the comments, empty lines, etc. That information is not while reading
     481             :  * the input file, so when saving the file back, it saves raw data.
     482             :  *
     483             :  * It is expected that you use this function only for configuration files
     484             :  * used for things other than administrative configuration files.
     485             :  *
     486             :  * \warning
     487             :  * The \p override_file flag is ignored if the override_filename is
     488             :  * not defined in this configuration file. In other words, if you create
     489             :  * a configuration file without an override, this function cannot then
     490             :  * save the newdata in an override file.
     491             :  *
     492             :  * \warning
     493             :  * If the configuration filename does not include any period or slash,
     494             :  * it is considered to be a well known configuration filename and in that
     495             :  * case the \p override_file is ignored since the name is built using
     496             :  * the main configuration filename (by adding /snapwebsites.d/ to the
     497             :  * path of the configuration files.)
     498             :  *
     499             :  * \note
     500             :  * See also the manager::replace_configuration_value() function in
     501             :  * snapmanager/lib/installer.cpp (we may at some point want to move
     502             :  * that code to the main snap library or even a contrib library to
     503             :  * allow for easy editing of configuration files.)
     504             :  *
     505             :  * \param[in] override_file  Whether the override filename is used to save
     506             :  *            this configuration back to drive.
     507             :  *
     508             :  * \return true if the configuration was saved successfully.
     509             :  */
     510           0 : bool snap_config_file::write_config_file(bool override_file)
     511             : {
     512             :     // the g_configuration_has_started should already be true since
     513             :     // a read should always happen before a write, but just in case
     514             :     // set that variable to true now
     515             :     //
     516           0 :     g_configuration_has_started = true;
     517             : 
     518             :     // if the filename includes any "." or "/", it is not one of our
     519             :     // files so we instead load the file as is (i.e. filename is
     520             :     // expected to be a full name, so we ignore our path)
     521             :     //
     522           0 :     std::string::size_type const pos(f_configuration_filename.find_first_of("./"));
     523           0 :     if(pos != std::string::npos)
     524             :     {
     525           0 :         if(override_file && !f_override_filename.empty())
     526             :         {
     527           0 :             return actual_write_config_file(f_override_filename);
     528             :         }
     529             :         else
     530             :         {
     531           0 :             return actual_write_config_file(f_configuration_filename);
     532             :         }
     533             :     }
     534             :     else
     535             :     {
     536           0 :         if(override_file)
     537             :         {
     538           0 :             return actual_write_config_file(g_configurations_path + "/snapwebsites.d/" + f_configuration_filename + ".conf");
     539             :         }
     540             :         else
     541             :         {
     542           0 :             return actual_write_config_file(g_configurations_path + "/" + f_configuration_filename + ".conf");
     543             :         }
     544             :     }
     545             : }
     546             : 
     547             : 
     548             : /** \brief Write the configuration to file.
     549             :  *
     550             :  * This is the function that actually writes the configuration data to file.
     551             :  * We use a sub-function so that way we can handle multiple cases in a clear
     552             :  * manner in the main write_config_file() function.
     553             :  *
     554             :  * \param[in] filename  The name of the file to read from.
     555             :  *
     556             :  * \return true if written, false on failure to write file.
     557             :  *
     558             :  * \sa write_config_file()
     559             :  */
     560           0 : bool snap_config_file::actual_write_config_file(std::string const & filename)
     561             : {
     562             :     // write to the configuration file now
     563             :     //
     564           0 :     QLockFile c(QString::fromUtf8(filename.c_str()));
     565           0 :     if(!c.open(QIODevice::WriteOnly))
     566             :     {
     567             :         // could not write here, it may be an EPERM
     568             :         //
     569           0 :         int const e(errno);
     570           0 :         SNAP_LOG_WARNING("could not open \"")(filename)("\" for writing. (errno: ")(e)("--")(strerror(e))(")");
     571           0 :         return false;
     572             :     }
     573             : 
     574           0 :     QDateTime now(QDateTime::currentDateTime());
     575             : 
     576             :     // read the configuration file variables as parameters
     577             :     //
     578           0 :     c.write(QString("# This file was auto-generated by snap_config.cpp on %1 at %2.\n"
     579             :             "# Making modifications here is likely safe unless the tool handling this\n"
     580             :             "# configuration file is actively working on it while you do the edits.\n")
     581           0 :                     .arg(now.toString("yyyy/MM/dd"))
     582           0 :                     .arg(now.toString("HH:mm:ss"))
     583           0 :                     .toUtf8());
     584             : 
     585             :     // then write on line per parameter
     586             :     //
     587           0 :     for(auto p : f_parameters)
     588             :     {
     589             :         // make sure that the field name and content do not include newline
     590             :         // characters, instead we replace them with the same syntax as in
     591             :         // C/C++ so '\\' and 'n',
     592             :         //
     593           0 :         std::string::size_type const pos(p.first.find_first_of("./"));
     594           0 :         if(pos == std::string::npos)
     595             :         {
     596           0 :             QByteArray data(p.first.c_str());
     597           0 :             data += '=';
     598           0 :             QByteArray value(p.second.c_str());
     599           0 :             value.replace("\n", "\\n");
     600           0 :             data += value;
     601           0 :             data += '\n';  // our actual new line, do not replace this one
     602           0 :             c.write(data);
     603             :         }
     604             :         //else -- ignore parameters with invalid names
     605             :     }
     606             : 
     607           0 :     return true;
     608             : }
     609             : 
     610             : 
     611             : /** \brief Retrieve the value of this parameter.
     612             :  *
     613             :  * This function searches for the named parameter. If it exists, then
     614             :  * its value gets returned. If it does not exist, then an empty string
     615             :  * is returned.
     616             :  *
     617             :  * To know whether the parameter exists and its value is an empty string,
     618             :  * then call has_parameter().
     619             :  *
     620             :  * \param[in] parameter_name  The name of the parameter to retrieve.
     621             :  *
     622             :  * \return The value of the parameter or the empty string if the parameter
     623             :  *         is not defined.
     624             :  *
     625             :  * \sa has_parameter()
     626             :  */
     627           0 : std::string snap_config_file::get_parameter(std::string const & parameter_name) const
     628             : {
     629           0 :     auto const & it(f_parameters.find(parameter_name));
     630           0 :     if(f_parameters.end() != it)
     631             :     {
     632           0 :         return it->second;
     633             :     }
     634             : 
     635           0 :     return std::string();
     636             : }
     637             : 
     638             : 
     639             : /** \brief Check whether this configuration file has a certain parameter.
     640             :  *
     641             :  * This function searches for the specified parameter by name and if
     642             :  * found return true, otherwise false.
     643             :  *
     644             :  * \warning
     645             :  * If you set that parameter, then this function will return true whether
     646             :  * the parameter was found in the original file or not.
     647             :  *
     648             :  * \param[in] name  The name of the parameter to search.
     649             :  *
     650             :  * \return true if the parameter is defined in this file.
     651             :  */
     652           0 : bool snap_config_file::has_parameter( std::string const & name ) const
     653             : {
     654           0 :     return f_parameters.find( name ) != f_parameters.end();
     655             : }
     656             : 
     657             : 
     658             : /** \brief Replace or create a parameter.
     659             :  *
     660             :  * This function saves the specified value in the named parameter.
     661             :  *
     662             :  * If the parameter did not exist yet, it exists upon return.
     663             :  *
     664             :  * \param[in] parameter_name  The name of the parameter to retrieve.
     665             :  * \param[in] value  The parameter's value.
     666             :  *
     667             :  * \return The value of the parameter or the empty string if the parameter
     668             :  *         is not defined.
     669             :  *
     670             :  * \sa has_parameter()
     671             :  */
     672           0 : void snap_config_file::set_parameter(std::string const & parameter_name, std::string const & value)
     673             : {
     674           0 :     f_parameters[parameter_name] = value;
     675           0 : }
     676             : 
     677             : 
     678             : /** \brief Return a reference to all the parameters defined in this file.
     679             :  *
     680             :  * This function returns a reference to the parameter map defined in
     681             :  * this file. The map is not editable.
     682             :  *
     683             :  * \return The configuration file parameter map.
     684             :  */
     685           0 : snap_configurations::parameter_map_t const & snap_config_file::get_parameters() const
     686             : {
     687           0 :     return f_parameters;
     688             : }
     689             : 
     690             : 
     691             : /** \brief Add the specified params to the parameters.
     692             :  *
     693             :  * This function copies the specified parameters \p params to the
     694             :  * list of parameters of this config file.
     695             :  *
     696             :  * If the configuration file already had such a parameter, it gets
     697             :  * overwritten.
     698             :  *
     699             :  * \param[in] params  A map of parameters to save in this configuration file.
     700             :  */
     701           0 : void snap_config_file::set_parameters( snap_configurations::parameter_map_t const & params )
     702             : {
     703           0 :     f_parameters.insert(params.begin(), params.end());
     704           0 : }
     705             : 
     706             : 
     707             : /** \brief Get the named configuration file.
     708             :  *
     709             :  * This function retrieves the named configuration file. If the file is
     710             :  * not yet loaded, the function loads the file at this point.
     711             :  *
     712             :  * \param[in] configuration_filename  The name of the configuration file to retrieve.
     713             :  * \param[in] override_filename  The name of the override, that file is
     714             :  *            checked first and if a field exists in it, that value is used.
     715             :  * \param[in] quiet                   Silently fail if one cannot read the config file.
     716             :  *
     717             :  * \return The shared pointer to a snap_config_file object.
     718             :  */
     719           0 : snap_config_file::pointer_t get_configuration
     720             :     ( std::string const & configuration_filename
     721             :     , std::string const & override_filename
     722             :     , bool const quiet = false
     723             :     )
     724             : {
     725             :     auto const & it(std::find_if(
     726             :                       g_config_files.begin()
     727             :                     , g_config_files.end()
     728           0 :                     , [configuration_filename](auto const & configuration)
     729             :                     {
     730           0 :                         return configuration_filename == configuration.second->get_configuration_filename();
     731           0 :                     }));
     732           0 :     if(g_config_files.end() == it)
     733             :     {
     734             :         // we did not find that configuration, it was not yet loaded,
     735             :         // load it now
     736             :         //
     737           0 :         snap_config_file::pointer_t conf(std::make_shared<snap_config_file>(configuration_filename, override_filename));
     738           0 :         g_config_files[configuration_filename] = conf;
     739           0 :         conf->read_config_file();
     740           0 :         if( !quiet && !conf->exists() )
     741             :         {
     742             :             // Exit the process as we have a critical error.
     743             :             //
     744             :             throw snap_configurations_exception_config_error(
     745             :                       "loading configuration file \""
     746           0 :                     + configuration_filename
     747           0 :                     + "\" failed: File is missing.");
     748             :         }
     749           0 :         return conf;
     750             :     }
     751             : 
     752           0 :     if(it->second->get_override_filename() != override_filename)
     753             :     {
     754             :         // do not allow a configuration file to have varying overrides
     755             :         //
     756           0 :         std::stringstream ss;
     757             :         ss << "loading configuration file \""
     758             :            << configuration_filename
     759             :            << "\" with two different override filenames: \""
     760           0 :            << it->second->get_override_filename()
     761             :            << "\" and \""
     762             :            << override_filename
     763           0 :            << "\"";
     764           0 :         SNAP_LOG_FATAL(ss.str())(".");
     765           0 :         syslog( LOG_CRIT, "%s in snap_config.cpp: get_configuration()", ss.str().c_str() );
     766           0 :         throw snap_configurations_exception_config_error(ss.str());
     767             :     }
     768             : 
     769           0 :     return it->second;
     770             : }
     771             : 
     772             : 
     773             : 
     774             : }
     775             : // no name namespace
     776             : 
     777             : 
     778             : 
     779             : 
     780             : /** \brief Initialize the snap configuration object.
     781             :  *
     782             :  * The constructor is used to allow for deleting other constructors.
     783             :  */
     784           0 : snap_configurations::snap_configurations()
     785             : {
     786           0 : }
     787             : 
     788             : 
     789             : /** \brief Get an instance pointer to the configuration files.
     790             :  *
     791             :  * This function returns a shared pointer to the configuration
     792             :  * instance allocated for this process.
     793             :  *
     794             :  * Note that most of the configuration functions are not thread
     795             :  * safe. If you are working on a multithread application, make
     796             :  * sure to load all the configuration files you need at initialization
     797             :  * before you create threads, or make sure the other threads never
     798             :  * access the configuration data.
     799             :  *
     800             :  * \warning
     801             :  * The implementation of the snap_config objects is thread safe, but
     802             :  * only if you make sure that you call this function once before
     803             :  * you create any threads. In other words, this very function is
     804             :  * not actually thread safe.
     805             :  *
     806             :  * \return A pointer to the snap_configurations object.
     807             :  */
     808           0 : snap_configurations::pointer_t snap_configurations::get_instance()
     809             : {
     810           0 :     if(g_configurations == nullptr)
     811             :     {
     812             :         // WARNING: cannot use std::make_shared<>() because the singleton
     813             :         //          has a private constructor
     814             :         //
     815             : 
     816             :         // first do all allocations, so if one fails, it is exception safe
     817             :         //
     818             :         // WARNING: remember that shared_ptr<>() are not safe as is
     819             :         //          (see SNAP-507 for details)
     820             :         //
     821           0 :         std::shared_ptr<snap_configurations> configurations; // use reset(), see SNAP-507, can't use make_shared() because constructor is private
     822           0 :         configurations.reset(new snap_configurations());
     823             : 
     824           0 :         std::shared_ptr<snap_thread::snap_mutex> mutex(std::make_shared<snap_thread::snap_mutex>());
     825             : 
     826             :         // now that all allocations were done, save the results
     827             :         //
     828           0 :         g_configurations.swap(configurations);
     829           0 :         g_mutex.swap(mutex);
     830             :     }
     831             : 
     832           0 :     return g_configurations;
     833             : }
     834             : 
     835             : 
     836             : /** \brief Return a reference to the current configuration path.
     837             :  *
     838             :  * This function returns a reference to the configuration path used
     839             :  * by this process.
     840             :  *
     841             :  * \return A reference to the configuration path string.
     842             :  */
     843           0 : std::string const & snap_configurations::get_configuration_path() const
     844             : {
     845           0 :     snap_thread::snap_lock lock(*g_mutex);
     846           0 :     return g_configurations_path;
     847             : }
     848             : 
     849             : 
     850             : /** \brief Change the path to the configuration files.
     851             :  *
     852             :  * Some (should be all...) daemons may let the administrator specify
     853             :  * the path to the configuration files. This path has to be set early,
     854             :  * before you read any configuration file (after, it will throw.)
     855             :  *
     856             :  * The path is used to read all the files.
     857             :  *
     858             :  * \exception snap_configurations_exception_too_late
     859             :  * This exception is raised if the function gets called after one of
     860             :  * the functions that allows to read data from the configuration file.
     861             :  * Generally, you want to call this function very early on in your
     862             :  * initialization process.
     863             :  *
     864             :  * \param[in] path  The new path.
     865             :  */
     866           0 : void snap_configurations::set_configuration_path(std::string const & path)
     867             : {
     868           0 :     snap_thread::snap_lock lock(*g_mutex);
     869             : 
     870             :     // prevent changing the path once we started loading files.
     871             :     //
     872           0 :     if(g_configuration_has_started)
     873             :     {
     874           0 :         throw snap_configurations_exception_too_late("snap_configurations::set_configuration_path() cannot be called once a configuration file was read.");
     875             :     }
     876             : 
     877             :     // other functions will not deal with with "" as the current directory
     878             :     // so make sure we use "." instead
     879             :     //
     880           0 :     if(path.empty())
     881             :     {
     882           0 :         g_configurations_path = ".";
     883             :     }
     884             :     else
     885             :     {
     886           0 :         g_configurations_path = path;
     887             :     }
     888           0 : }
     889             : 
     890             : 
     891             : /** \brief Get a constant reference to all the parameters.
     892             :  *
     893             :  * Once in a while it may be useful to gain access to the entire list
     894             :  * of parameters defined in a configuration file. This function
     895             :  * gives you that ability.
     896             :  *
     897             :  * Since you get a reference, if you do not create a copy, you will
     898             :  * see any changes to parameters that are made by other functions.
     899             :  *
     900             :  * \param[in] configuration_filename  The name of the configuration file off of
     901             :  *                                which the parameters are returned.
     902             :  * \param[in] override_filename  The name of the override, that file is
     903             :  *            checked first and if a field exists in it, that value is used.
     904             :  *
     905             :  * \return A reference to the map of parameters of that configuration file.
     906             :  */
     907           0 : snap_configurations::parameter_map_t const & snap_configurations::get_parameters(std::string const & configuration_filename, std::string const & override_filename) const
     908             : {
     909           0 :     snap_thread::snap_lock lock(*g_mutex);
     910           0 :     auto const config(get_configuration(configuration_filename, override_filename));
     911           0 :     return config->get_parameters();
     912             : }
     913             : 
     914             : 
     915             : /** \brief Replace the parameters of this configuration file with new ones.
     916             :  *
     917             :  * This function replaces the existing parameters with the new specified
     918             :  * ones in \p params. This is most often used to copy the command line
     919             :  * parameters in the configuration file, as if the command line parameters
     920             :  * had been read from that configuration.
     921             :  *
     922             :  * The parameters specified to this function have precedence over the
     923             :  * parameters read from the file (i.e. they overwrite any existing
     924             :  * parameter.)
     925             :  *
     926             :  * \param[in] configuration_filename  The name of the configuration file concerned.
     927             :  * \param[in] override_filename  The name of the override, that file is
     928             :  *            checked first and if a field exists in it, that value is used.
     929             :  * \param[in] params  The new parameters.
     930             :  */
     931           0 : void snap_configurations::set_parameters(std::string const & configuration_filename, std::string const & override_filename, parameter_map_t const & params)
     932             : {
     933           0 :     snap_thread::snap_lock lock(*g_mutex);
     934           0 :     auto const config(get_configuration(configuration_filename, override_filename));
     935           0 :     config->set_parameters(params);
     936           0 : }
     937             : 
     938             : 
     939             : /** \brief Retreve a parameter from the configuration file.
     940             :  *
     941             :  * This function reads the specified \p configuration_filename file and then
     942             :  * searches for the specified \p parameter_name. If found, then its
     943             :  * value is returned, otherwise the function returns an empty string.
     944             :  *
     945             :  * To know whether a parameter is defined (opposed to being empty),
     946             :  * use the has_parameter() function instead.
     947             :  *
     948             :  * \param[in] configuration_filename  The name of the configuration file.
     949             :  * \param[in] override_filename  The name of the override, that file is
     950             :  *            checked first and if a field exists in it, that value is used.
     951             :  * \param[in] parameter_name  The name of the parameter to retrieve.
     952             :  *
     953             :  * \return The value of the parameter or the empty string.
     954             :  */
     955           0 : std::string snap_configurations::get_parameter(std::string const & configuration_filename, std::string const & override_filename, std::string const & parameter_name) const
     956             : {
     957           0 :     snap_thread::snap_lock lock(*g_mutex);
     958           0 :     auto const config(get_configuration(configuration_filename, override_filename));
     959           0 :     return config->get_parameter(parameter_name);
     960             : }
     961             : 
     962             : 
     963             : /** \brief Check whether the specified configuration file exists.
     964             :  *
     965             :  * This function searches for the configuration file and possibly an
     966             :  * override file. If neither exists, the function returns false. If at
     967             :  * least one exists, then the function returns false.
     968             :  *
     969             :  * \param[in] configuration_filename  The name of the configuration file to
     970             :  *            probe for existence.
     971             :  * \param[in] override_filename  The name of the override, that file is
     972             :  *            checked first and if a field exists in it, that value is used.
     973             :  *
     974             :  * \return true if a configuration file was found.
     975             :  */
     976           0 : bool snap_configurations::configuration_file_exists( std::string const & configuration_filename, std::string const &override_filename )
     977             : {
     978           0 :     snap_thread::snap_lock lock(*g_mutex);
     979           0 :     auto const config(get_configuration(configuration_filename, override_filename, true/*quiet*/));
     980           0 :     return config->exists();
     981             : }
     982             : 
     983             : 
     984             : /** \brief Check whether a certain configuration file has a certain parameter.
     985             :  *
     986             :  * This function reads the specified configuration file and then check
     987             :  * whether it defines the specified parameter. If so, it returns true.
     988             :  * If not, it returns false.
     989             :  *
     990             :  * \warning
     991             :  * Note that this function forces a read of the specified configuration
     992             :  * file since the only way to know whether that parameter exists in the
     993             :  * configuration is to read it.
     994             :  *
     995             :  * \param[in] configuration_filename  The name of the configuration file to read.
     996             :  * \param[in] override_filename  The name of the override, that file is
     997             :  *            checked first and if a field exists in it, that value is used.
     998             :  * \param[in] parameter_name  The parameter to check the presence of.
     999             :  */
    1000           0 : bool snap_configurations::has_parameter(std::string const & configuration_filename, std::string const & override_filename, std::string const & parameter_name) const
    1001             : {
    1002           0 :     snap_thread::snap_lock lock(*g_mutex);
    1003           0 :     auto const config(get_configuration(configuration_filename, override_filename));
    1004           0 :     return config->has_parameter(parameter_name);
    1005             : }
    1006             : 
    1007             : 
    1008             : /** \brief Replace the value of one parameter.
    1009             :  *
    1010             :  * This function replaces the value of parameter \p parameter_name
    1011             :  * in configuration file \p configuration_filename with \p value.
    1012             :  *
    1013             :  * \param[in] configuration_filename  The name of the configuration file to change.
    1014             :  * \param[in] override_filename  The name of the override, that file is
    1015             :  *            checked first and if a field exists in it, that value is used.
    1016             :  * \param[in] parameter_name  The name of the parameter to modify.
    1017             :  * \param[in] value  The new value of the parameter.
    1018             :  */
    1019           0 : void snap_configurations::set_parameter(std::string const & configuration_filename, std::string const & override_filename, std::string const & parameter_name, std::string const & value)
    1020             : {
    1021           0 :     snap_thread::snap_lock lock(*g_mutex);
    1022           0 :     auto const config(get_configuration(configuration_filename, override_filename));
    1023           0 :     config->set_parameter(parameter_name, value);
    1024           0 : }
    1025             : 
    1026             : 
    1027             : /** \brief Save fields back to the configuration file.
    1028             :  *
    1029             :  * This function can be used to save the configuration file back to file.
    1030             :  * In most cases you want to use the override filename (so on the snap_config
    1031             :  * you would use "true" as the first parameter.)
    1032             :  *
    1033             :  * Note that the in memory configuration parameters do NOT include any
    1034             :  * comments. The saved file gets a comment at the top saying it was
    1035             :  * auto-generated. This feature should only be used for configuration files
    1036             :  * that administrators do not expected to update themselves or are being
    1037             :  * updated in the override sub-folder.
    1038             :  *
    1039             :  * \note
    1040             :  * The configuration_filename and override_filename parameters are used to
    1041             :  * find the configuration file in our cache (or add it there if not already
    1042             :  * present.) Since the corresponding filenames are already defined in the
    1043             :  * snap_config_file object, it is not used to save the file per se, only
    1044             :  * to find the snap_config_file that corresponds to the input parameters.
    1045             :  *
    1046             :  * \param[in] configuration_filename  The name of the configuration file to change.
    1047             :  * \param[in] override_filename  The name of the override, that file is
    1048             :  *            checked first and if a field exists in it, that value is used.
    1049             :  * \param[in] override_file  Whether the save uses the \p configuration_filename
    1050             :  *            or the \p override_filename to save this 
    1051             :  */
    1052           0 : bool snap_configurations::save(std::string const & configuration_filename, std::string const & override_filename, bool override_file)
    1053             : {
    1054           0 :     snap_thread::snap_lock lock(*g_mutex);
    1055           0 :     auto const config(get_configuration(configuration_filename, override_filename, true));
    1056           0 :     return config->write_config_file(override_file);
    1057             : }
    1058             : 
    1059             : 
    1060           6 : }
    1061             : //namespace snap
    1062             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13