LCOV - code coverage report
Current view: top level - snapwebsites - flags.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 210 0.5 %
Date: 2019-12-15 17:13:15 Functions: 2 35 5.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Snap Websites Server -- snapwebsites library -- flags handling
       2             : // Copyright (c) 2018-2019  Made to Order Software Corp.  All Rights Reserved
       3             : //
       4             : // https://snapwebsites.org/
       5             : // contact@m2osw.com
       6             : //
       7             : // This program is free software; you can redistribute it and/or modify
       8             : // it under the terms of the GNU General Public License as published by
       9             : // the Free Software Foundation; either version 2 of the License, or
      10             : // (at your option) any later version.
      11             : //
      12             : // This program is distributed in the hope that it will be useful,
      13             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             : // GNU General Public License for more details.
      16             : //
      17             : // You should have received a copy of the GNU General Public License
      18             : // along with this program; if not, write to the Free Software
      19             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      20             : 
      21             : 
      22             : // self
      23             : //
      24             : #include "./flags.h"
      25             : 
      26             : 
      27             : // snapwebsites lib
      28             : //
      29             : #include <snapwebsites/glob_dir.h>
      30             : #include <snapwebsites/log.h>
      31             : #include <snapwebsites/snap_config.h>
      32             : #include <snapwebsites/snapwebsites.h>
      33             : 
      34             : 
      35             : // snapdev lib
      36             : //
      37             : #include <snapdev/not_used.h>
      38             : #include <snapdev/tokenize_string.h>
      39             : 
      40             : 
      41             : // boost lib
      42             : //
      43             : #include <boost/algorithm/string.hpp>
      44             : 
      45             : 
      46             : // last include
      47             : //
      48             : #include <snapdev/poison.h>
      49             : 
      50             : 
      51             : 
      52             : 
      53             : namespace snap
      54             : {
      55             : 
      56             : 
      57             : 
      58             : namespace
      59             : {
      60             : 
      61             : 
      62             : 
      63             : }
      64             : // no name namespace
      65             : 
      66             : 
      67             : 
      68             : 
      69             : 
      70             : /** \brief Initialize a "new" flag.
      71             :  *
      72             :  * This function creates a new flag in memory.
      73             :  *
      74             :  * New flags are generally created using one of the SNAP_FLAG_UP()
      75             :  * or the SNAP_FLAG_DOWN() macros, which will automatically
      76             :  * initialize the flag, especially the source filename, the function name,
      77             :  * and the line number where the flag is being created, and the status
      78             :  * which the macro describes.
      79             :  *
      80             :  * All the names must match the following macro:
      81             :  *
      82             :  * \code
      83             :  *      [a-zA-Z][-a-zA-Z0-9]*
      84             :  * \endcode
      85             :  *
      86             :  * The underscore is not included in a name because we want to be able to
      87             :  * separate multiple names using the underscore, which is what is used
      88             :  * when building the filename from this information.
      89             :  *
      90             :  * \param[in] unit  The name of the unit creating this flag. For example,
      91             :  *            the core plugins would use "core-plugin".
      92             :  * \param[in] section  The name of the section creating this flag. In case
      93             :  *            of the core plugin, this should be the name of the plugin.
      94             :  * \param[in] name  The actual name of the flag. This is expected to
      95             :  *            somewhat describe what the flag is being for.
      96             :  */
      97           0 : snap_flag::snap_flag(std::string const & unit, std::string const & section, std::string const & name)
      98             :     : f_unit(unit)
      99             :     , f_section(section)
     100           0 :     , f_name(name)
     101             : {
     102           0 :     valid_name(f_unit);
     103           0 :     valid_name(f_section);
     104           0 :     valid_name(f_name);
     105           0 : }
     106             : 
     107             : 
     108             : /** \brief Load a flag from file.
     109             :  *
     110             :  * When this constructor is used, the flag gets loaded from file.
     111             :  * Flags use a snap_config file to handle their permanent data.
     112             :  *
     113             :  * The fields offered in the snap_flag object are translated
     114             :  * in a configuration field. This function reads the data with
     115             :  * a snap_file object and converts it to snap_flag data.
     116             :  *
     117             :  * \param[in] filename  The nane of the file to load.
     118             :  */
     119           0 : snap_flag::snap_flag(std::string const & filename)
     120           0 :     : f_filename(filename)
     121             : {
     122           0 :     if(f_filename.empty())
     123             :     {
     124           0 :         throw flags_exception_invalid_parameter("the filename must be defined (i.e. not empty) when using the flag constructor with a filename");
     125             :     }
     126             : 
     127           0 :     snap_config flag(get_filename());
     128             : 
     129           0 :     if(!flag.has_parameter("unit")
     130           0 :     || !flag.has_parameter("section")
     131           0 :     || !flag.has_parameter("name")
     132           0 :     || !flag.has_parameter("message"))
     133             :     {
     134           0 :         throw flags_exception_invalid_parameter("a flag file is expecteda unit, section, and name field, along with a message field. Other fields are optional.");
     135             :     }
     136             : 
     137           0 :     f_unit = flag.get_parameter("unit");
     138           0 :     f_section = flag.get_parameter("section");
     139           0 :     f_name = flag.get_parameter("name");
     140             : 
     141           0 :     if(flag.has_parameter("source_file"))
     142             :     {
     143           0 :         f_source_file = flag.get_parameter("source_file");
     144             :     }
     145             : 
     146           0 :     if(flag.has_parameter("function"))
     147             :     {
     148           0 :         f_function = flag.get_parameter("function");
     149             :     }
     150             : 
     151           0 :     if(flag.has_parameter("line"))
     152             :     {
     153           0 :         f_line = std::stol(flag.get_parameter("line"));
     154             :     }
     155             : 
     156           0 :     f_message = flag.get_parameter("message");
     157             : 
     158           0 :     if(flag.has_parameter("priority"))
     159             :     {
     160           0 :         f_priority = std::stol(flag.get_parameter("priority"));
     161             :     }
     162             : 
     163           0 :     if(flag.has_parameter("manual_down"))
     164             :     {
     165           0 :         f_manual_down = flag.get_parameter("manual_down") == "yes";
     166             :     }
     167             : 
     168           0 :     if(flag.has_parameter("date"))
     169             :     {
     170           0 :         f_date = std::stol(flag.get_parameter("date"));
     171             :     }
     172             : 
     173           0 :     if(flag.has_parameter("modified"))
     174             :     {
     175           0 :         f_modified = std::stol(flag.get_parameter("modified"));
     176             :     }
     177             : 
     178           0 :     if(flag.has_parameter("tags"))
     179             :     {
     180             :         // here we use an intermediate tag_list vector so the tokenize_string
     181             :         // works then add those string in the f_tags parameter
     182             :         //
     183           0 :         std::string const tags(flag.get_parameter("tags"));
     184           0 :         std::vector<std::string> tag_list;
     185           0 :         tokenize_string(tag_list
     186             :                       , tags
     187             :                       , ","
     188             :                       , true
     189             :                       , " \n\r\t");
     190           0 :         f_tags.insert(tag_list.begin(), tag_list.end());
     191             :     }
     192             : 
     193           0 :     if(flag.has_parameter("hostname"))
     194             :     {
     195           0 :         f_hostname = flag.get_parameter("hostname");
     196             :     }
     197             : 
     198           0 :     if(flag.has_parameter("count"))
     199             :     {
     200           0 :         f_count = std::stol(flag.get_parameter("count"));
     201             :     }
     202             : 
     203           0 :     if(flag.has_parameter("version"))
     204             :     {
     205           0 :         f_version = flag.get_parameter("version");
     206             :     }
     207           0 : }
     208             : 
     209             : 
     210             : /** \brief Set the state of the flag.
     211             :  *
     212             :  * At the moment, the flag can be UP or DOWN. By default it is UP meaning
     213             :  * that there is an error, something the administrator has to take care of
     214             :  * to make sure the system works as expected. For example, the antivirus
     215             :  * backend will detect that the clamav package is not installed and install
     216             :  * it as required.
     217             :  *
     218             :  * \param[in] state  The new state.
     219             :  *
     220             :  * \return A reference to this.
     221             :  */
     222           0 : snap_flag & snap_flag::set_state(state_t state)
     223             : {
     224           0 :     f_state = state;
     225             : 
     226           0 :     return *this;
     227             : }
     228             : 
     229             : 
     230             : /** \brief Set the name of the source file.
     231             :  *
     232             :  * The name of the source where the flag is being raised is added using
     233             :  * this function.
     234             :  *
     235             :  * \param[in] source_file  The source file name.
     236             :  *
     237             :  * \return A reference to this.
     238             :  */
     239           0 : snap_flag & snap_flag::set_source_file(std::string const & source_file)
     240             : {
     241           0 :     f_source_file = source_file;
     242             : 
     243           0 :     return *this;
     244             : }
     245             : 
     246             : 
     247             : /** \brief Set the name of the function raising the flag.
     248             :  *
     249             :  * For debug purposes, we save the name of the function that called
     250             :  * the manager function to save the function. It should help us,
     251             :  * long term, to find flags and maintain them as required.
     252             :  *
     253             :  * \param[in] function  The name of the concerned function.
     254             :  *
     255             :  * \return A reference to this.
     256             :  */
     257           0 : snap_flag & snap_flag::set_function(std::string const & function)
     258             : {
     259           0 :     f_function = function;
     260             : 
     261           0 :     return *this;
     262             : }
     263             : 
     264             : 
     265             : /** \brief Set the line number at which the event happened.
     266             :  *
     267             :  * This parameter is used to save the line at which the function
     268             :  * used one of the snap_flag macros.
     269             :  *
     270             :  * By default the value is set to zero. If never called, then this
     271             :  * is a way to know that no line number was defined.
     272             :  *
     273             :  * \param[in] line  The new line number at which this flag is being raised.
     274             :  *
     275             :  * \return A reference to this.
     276             :  */
     277           0 : snap_flag & snap_flag::set_line(int line)
     278             : {
     279           0 :     f_line = line;
     280             : 
     281           0 :     return *this;
     282             : }
     283             : 
     284             : 
     285             : /** \brief Set the error message.
     286             :  *
     287             :  * A flag is always accompagned by an error message of some sort.
     288             :  * For example, the sendmail backend checks whether postfix is
     289             :  * installed on that computer. If not, it raises a flag with an
     290             :  * error message saying something like this: "The sendmail backend
     291             :  * expects Postfix to be installed on the same computer. snapmta
     292             :  * is not good enough to support the full mail server."
     293             :  *
     294             :  * \param[in] message  The message explaining why the flag is raised.
     295             :  *
     296             :  * \return A reference to this.
     297             :  */
     298           0 : snap_flag & snap_flag::set_message(std::string const & message)
     299             : {
     300           0 :     f_message = message;
     301             : 
     302           0 :     return *this;
     303             : }
     304             : 
     305             : 
     306             : /** \brief Set the error message.
     307             :  *
     308             :  * A flag is always accompagned by an error message of some sort.
     309             :  * For example, the sendmail backend checks whether postfix is
     310             :  * installed on that computer. If not, it raises a flag with an
     311             :  * error message saying something like this: "The sendmail backend
     312             :  * expects Postfix to be installed on the same computer. snapmta
     313             :  * is not good enough to support the full mail server."
     314             :  *
     315             :  * \param[in] message  The message explaining why the flag is raised.
     316             :  *
     317             :  * \return A reference to this.
     318             :  */
     319           0 : snap_flag & snap_flag::set_message(QString const & message)
     320             : {
     321           0 :     f_message = message.toUtf8().data();
     322             : 
     323           0 :     return *this;
     324             : }
     325             : 
     326             : 
     327             : /** \brief Set the error message.
     328             :  *
     329             :  * A flag is always accompagned by an error message of some sort.
     330             :  * For example, the sendmail backend checks whether postfix is
     331             :  * installed on that computer. If not, it raises a flag with an
     332             :  * error message saying something like this: "The sendmail backend
     333             :  * expects Postfix to be installed on the same computer. snapmta
     334             :  * is not good enough to support the full mail server."
     335             :  *
     336             :  * \param[in] message  The message explaining why the flag is raised.
     337             :  *
     338             :  * \return A reference to this.
     339             :  */
     340           0 : snap_flag & snap_flag::set_message(char const * message)
     341             : {
     342           0 :     if(message == nullptr)
     343             :     {
     344           0 :         f_message.clear();
     345             :     }
     346             :     else
     347             :     {
     348           0 :         f_message = message;
     349             :     }
     350             : 
     351           0 :     return *this;
     352             : }
     353             : 
     354             : 
     355             : /** \brief Set the error message.
     356             :  *
     357             :  * A flag is always accompagned by an error message of some sort.
     358             :  * For example, the sendmail backend checks whether postfix is
     359             :  * installed on that computer. If not, it raises a flag with an
     360             :  * error message saying something like this: "The sendmail backend
     361             :  * expects Postfix to be installed on the same computer. snapmta
     362             :  * is not good enough to support the full mail server."
     363             :  *
     364             :  * The default priority is 5. It can be reduced or increased. It
     365             :  * is expected to be between 0 and 100.
     366             :  *
     367             :  * \param[in] priority  The error priority.
     368             :  *
     369             :  * \return A reference to this.
     370             :  */
     371           0 : snap_flag & snap_flag::set_priority(int priority)
     372             : {
     373           0 :     if(priority < 0)
     374             :     {
     375           0 :         f_priority = 0;
     376             :     }
     377           0 :     else if(priority > 100)
     378             :     {
     379           0 :         f_priority = 100;
     380             :     }
     381             :     else
     382             :     {
     383           0 :         f_priority = priority;
     384             :     }
     385             : 
     386           0 :     return *this;
     387             : }
     388             : 
     389             : 
     390             : /** \brief Mark whether a manual down is required for this flag.
     391             :  *
     392             :  * Some flags may be turned ON but never turned OFF. These are called
     393             :  * _manual flags_, because you have to turn them off manually.
     394             :  *
     395             :  * \todo
     396             :  * At some point, the Watchdog interface in the snapmanager.cgi will
     397             :  * allow you to click a link to manually take a flag down.
     398             :  *
     399             :  * \param[in] manual  Whether the flag is considered manual or not.
     400             :  *
     401             :  * \return A reference to this.
     402             :  */
     403           0 : snap_flag & snap_flag::set_manual_down(bool manual)
     404             : {
     405           0 :     f_manual_down = manual;
     406             : 
     407           0 :     return *this;
     408             : }
     409             : 
     410             : 
     411             : /** \brief Add a tag to the list of tags of this flag.
     412             :  *
     413             :  * You can assign tags to a flag so as to group it with other flags
     414             :  * that reuse the same tag.
     415             :  *
     416             :  * The names must be valid names (as the unit, section, and flag names.)
     417             :  * Your name must validate against this regular expression:
     418             :  *
     419             :  * \code
     420             :  *      [a-zA-Z][-a-zA-Z0-9]*
     421             :  * \endcode
     422             :  *
     423             :  * So a-z, A-Z, 0-9, and dash. The first character must be a letter.
     424             :  *
     425             :  * Note that the underscore (_) is not included because we use those
     426             :  * to separate each word in a filename.
     427             :  *
     428             :  * \param[in] tag  The name of a tag to add to this flag.
     429             :  *
     430             :  * \return A reference to this.
     431             :  */
     432           0 : snap_flag & snap_flag::add_tag(std::string const & tag)
     433             : {
     434           0 :     f_tags.insert(tag);
     435             : 
     436           0 :     return *this;
     437             : }
     438             : 
     439             : 
     440             : /** \brief Get the current state.
     441             :  *
     442             :  * Get the state of the flag file.
     443             :  *
     444             :  * A flag file can be UP or DOWN. When DOWN, a save() will delete the
     445             :  * file. When UP, the file gets created.
     446             :  *
     447             :  * \return The current state of the flag.
     448             :  */
     449           0 : snap_flag::state_t snap_flag::get_state() const
     450             : {
     451           0 :     return f_state;
     452             : }
     453             : 
     454             : 
     455             : /** \brief Get the unit name.
     456             :  *
     457             :  * Flags are made unique by assigning them unit and section names.
     458             :  *
     459             :  * The unit name would be something such as "core-plugins" for the
     460             :  * main snapserver core plugins.
     461             :  *
     462             :  * \return The unit name.
     463             :  *
     464             :  * \sa get_section()
     465             :  * \sa get_name()
     466             :  */
     467           0 : std::string const & snap_flag::get_unit() const
     468             : {
     469           0 :     return f_unit;
     470             : }
     471             : 
     472             : 
     473             : /** \brief Get the section name.
     474             :  *
     475             :  * The section name defines the part of the software that has a problem.
     476             :  * For example, for the core plugins, you may want to use the name of the
     477             :  * plugin.
     478             :  *
     479             :  * \return The name of the section raising the flag.
     480             :  *
     481             :  * \sa get_unit()
     482             :  * \sa get_name()
     483             :  */
     484           0 : std::string const & snap_flag::get_section() const
     485             : {
     486           0 :     return f_section;
     487             : }
     488             : 
     489             : 
     490             : /** \brief Name of the flag.
     491             :  *
     492             :  * This parameter defines the name of the flag. The reason for the error
     493             :  * is often what is used here. The name must be short and ASCII only, but
     494             :  * should still properly define why the error occurs.
     495             :  *
     496             :  * A more detailed error message is returned by get_message().
     497             :  *
     498             :  * The get_unit() and get_section() define more generic names than this
     499             :  * one.
     500             :  *
     501             :  * For example, the attachment plugin checks for virus infected attachments.
     502             :  * This requires the clamav package to be installed. If not installed, it
     503             :  * raises a flag. That flag is part of the "core-plugins" (unit name),
     504             :  * and it gets raised in the "attachment" (section name) plugin, and it gets
     505             :  * raised because of "clamav-missing" (flag name)
     506             :  *
     507             :  * \return The name of the flag.
     508             :  *
     509             :  * \sa get_message()
     510             :  */
     511           0 : std::string const & snap_flag::get_name() const
     512             : {
     513           0 :     return f_name;
     514             : }
     515             : 
     516             : 
     517             : /** \brief Retrieve the name of the source file.
     518             :  *
     519             :  * This function retrieves the source filename. This is set using the
     520             :  * macros. It helps finding the reason for the falg being raised if
     521             :  * the message is not clear enough.
     522             :  *
     523             :  * \return The source file name.
     524             :  */
     525           0 : std::string const & snap_flag::get_source_file() const
     526             : {
     527           0 :     return f_source_file;
     528             : }
     529             : 
     530             : 
     531             : /** \brief Get the filename.
     532             :  *
     533             :  * This function returns the filename used to access this flag.
     534             :  *
     535             :  * If you loaded the flag files, then this is defined from the constructor.
     536             :  *
     537             :  * If you created a snap_flag object from scratch, then the filename
     538             :  * is built from the unit, section, and flag names as follow:
     539             :  *
     540             :  * \code
     541             :  *      <unit> + '_' + <section> + '_' + <flag name> + ".flag"
     542             :  * \endcode
     543             :  *
     544             :  */
     545           0 : std::string const & snap_flag::get_filename() const
     546             : {
     547           0 :     if(f_filename.empty())
     548             :     {
     549           0 :         snap_config server_config("snapserver");
     550           0 :         std::string path("/var/lib/snapwebsites/flags");
     551           0 :         if(server_config.has_parameter("flag_path"))
     552             :         {
     553           0 :             path = server_config["flag_path"];
     554             :         }
     555           0 :         f_filename = path + "/" + f_unit + "_" + f_section + "_" + f_name + ".flag";
     556             :     }
     557           0 :     return f_filename;
     558             : }
     559             : 
     560             : 
     561             : /** \brief Retrieve the function name.
     562             :  *
     563             :  * The function name defines the name of the function where the macro
     564             :  * was used. It can be useful for debugging where aproblem happens.
     565             :  *
     566             :  * \return The name of the function we are that was tike
     567             :  */
     568           0 : std::string const & snap_flag::get_function() const
     569             : {
     570           0 :     return f_function;
     571             : }
     572             : 
     573             : 
     574             : /** \brief Retrieve the line number at which it was first called.
     575             :  *
     576             :  * This is for debug purposes so one can easily find exactly what code
     577             :  * generated which flag.
     578             :  *
     579             :  * \return The line number where th snap_flag object is created.
     580             :  */
     581           0 : int snap_flag::get_line() const
     582             : {
     583           0 :     return f_line;
     584             : }
     585             : 
     586             : 
     587             : /** \brief The actual error message of this flag.
     588             :  *
     589             :  * A flag is used to tell the snapwatchdog flag plugin that something
     590             :  * is wrong and requires the administrator to fix it up.
     591             :  *
     592             :  * The message should be plain text. It may include newline characters.
     593             :  *
     594             :  * \return The message of this flag.
     595             :  */
     596           0 : std::string const & snap_flag::get_message() const
     597             : {
     598           0 :     return f_message;
     599             : }
     600             : 
     601             : 
     602             : /** \brief Retrieve the flag priority.
     603             :  *
     604             :  * The function returns the priority of the flag. By default it is set to 5.
     605             :  * If you want to increase the priority so the error shows up in an email,
     606             :  * increase the priority to at least 50. Remember that a really high priority
     607             :  * (close to 100) will increase the number of emails. Watch out as it could
     608             :  * both the admninistrators.
     609             :  *
     610             :  * When displaying the flags, the highest priority is used and a single
     611             :  * message is sent for all the priorities.
     612             :  *
     613             :  * \return This flag priority.
     614             :  */
     615           0 : int snap_flag::get_priority() const
     616             : {
     617           0 :     return f_priority;
     618             : }
     619             : 
     620             : 
     621             : /** \brief Check whether the flag is considered manual or automatic.
     622             :  *
     623             :  * A _manual down_ flag is a flag that the administrator has to turn
     624             :  * off manually once the problem was taken cared off.
     625             :  *
     626             :  * By default, a snap_flag is considered automatic, which means
     627             :  * that the process that raises the flag up for some circumstances
     628             :  * will also know how to bring that flag down once the circumstances
     629             :  * disappear.
     630             :  *
     631             :  * This function returns true if the process will never bring its
     632             :  * flag down automatically. This is particularly true if the process
     633             :  * use the SNAP_FLAG_UP() macro but never uses the corresponding
     634             :  * SNAP_FLAG_DOWN()--corresponding as in with the same first
     635             :  * three strings (unit, section, name.)
     636             :  *
     637             :  * \return true if the flag has to be taken down (deleted) manually.
     638             :  *
     639             :  * \sa set_manual_down()
     640             :  */
     641           0 : bool snap_flag::get_manual_down() const
     642             : {
     643           0 :     return f_manual_down;
     644             : }
     645             : 
     646             : 
     647             : /** \brief Retrieve the date when the flag was first raised.
     648             :  *
     649             :  * The function returns the date when the flag was first raised. A flag
     650             :  * that often goes up and down will have the date when it last went up.
     651             :  *
     652             :  * See get_modified() to get the date when the flag was last checked.
     653             :  * In some cases, checks are done once each time a command is run.
     654             :  * In other cases, checks are performed by the startup code of a
     655             :  * daemon so the modification date is likely to not change for a while.
     656             :  *
     657             :  * \return This date when this flag was last raised.
     658             :  */
     659           0 : time_t snap_flag::get_date() const
     660             : {
     661           0 :     return f_date;
     662             : }
     663             : 
     664             : 
     665             : /** \brief Retrieve the date when the flag was last checked.
     666             :  *
     667             :  * The function returns the date when the code raising this flag was last
     668             :  * run.
     669             :  *
     670             :  * This indicates when the flag was last updated. If very recent then we
     671             :  * know that the problem that the flag raised is likely still in force.
     672             :  * A modified date which is really old may mean that the code testing
     673             :  * this problem does not automatically take the flag down (a bug in itself).
     674             :  *
     675             :  * Note that some flags are checked only once at boot time, or once
     676             :  * when a service starts. So it is not abnormal to see a raised flag
     677             :  * modification date remain the same for a very long time.
     678             :  *
     679             :  * \return This date when this flag's problem was last checked.
     680             :  */
     681           0 : time_t snap_flag::get_modified() const
     682             : {
     683           0 :     return f_modified;
     684             : }
     685             : 
     686             : 
     687             : /** \brief Return a reference to the list of tags.
     688             :  *
     689             :  * A flag can be given a list of tags in order to group it with other
     690             :  * flags that may not be of the same unit or section.
     691             :  *
     692             :  * \return The set of tags attached to this flag.
     693             :  */
     694           0 : snap_flag::tag_list_t const & snap_flag::get_tags() const
     695             : {
     696           0 :     return f_tags;
     697             : }
     698             : 
     699             : 
     700             : /** \brief The name of the computer on which this flag was generated.
     701             :  *
     702             :  * In order to be able to distinguish on which computer the flag was
     703             :  * raised, the save() function includes the hostname of the computer
     704             :  * in the flag file.
     705             :  *
     706             :  * \return The hostname of the computer that saved this flag file.
     707             :  */
     708           0 : std::string const & snap_flag::get_hostname() const
     709             : {
     710           0 :     return f_hostname;
     711             : }
     712             : 
     713             : 
     714             : /** \brief Retrieve the number of times this flag was raised.
     715             :  *
     716             :  * Each time a flag gets raised this counter is increased by 1. It starts
     717             :  * at 0 so the very first time it gets saved, the counter will be 1.
     718             :  *
     719             :  * This is an indicator of how many times the flag situation was found to
     720             :  * be true. In most cases this should be a really small number.
     721             :  *
     722             :  * \return The number of times the flag file was saved.
     723             :  */
     724           0 : int snap_flag::get_count() const
     725             : {
     726           0 :     return f_count;
     727             : }
     728             : 
     729             : 
     730             : /** \brief Get the version used to create this flag file.
     731             :  *
     732             :  * When the flag file gets saved, the current version of snapwebsites gets
     733             :  * saved in the file as the "version" field. This function returns that
     734             :  * value.
     735             :  *
     736             :  * Note that if the file gets updated, then the version of the newest write
     737             :  * is used in the file.
     738             :  *
     739             :  * \return The version used to save the flag file.
     740             :  */
     741           0 : std::string const & snap_flag::get_version() const
     742             : {
     743           0 :     return f_version;
     744             : }
     745             : 
     746             : 
     747             : /** \brief Save the data to file.
     748             :  *
     749             :  * This function is used to save the flag to file.
     750             :  *
     751             :  * Note that if  the status is DOWN, then the file gets deleted.
     752             :  *
     753             :  * The file format used is the same as our configuration files:
     754             :  *
     755             :  * \code
     756             :  *      <varname>=<value>
     757             :  * \endcode
     758             :  *
     759             :  * These files should not be edited by administrators, though, since
     760             :  * they are just handled automatically by the code that generates this
     761             :  * data.
     762             :  *
     763             :  * \attention
     764             :  * Your implementation of the flags must make sure to use the
     765             :  * SNAP_FLAG_UP() when an error is detected and use
     766             :  * the SNAP_FLAG_DOWN() when the error is not detected
     767             :  * anymore. This is important since the file needs to disappear
     768             :  * once the problem was resolved.
     769             :  *
     770             :  * \return true if the save succeeded.
     771             :  */
     772           0 : bool snap_flag::save()
     773             : {
     774           0 :     bool result(true);
     775             : 
     776           0 :     if(f_state == state_t::STATE_UP)
     777             :     {
     778             :         // create and config object
     779             :         //
     780           0 :         snap_config flag(get_filename());
     781             : 
     782             :         // if the file exists, check whether a "date" is defined
     783             :         //
     784           0 :         bool const exists(flag.configuration_file_exists());
     785           0 :         bool has_date(false);
     786           0 :         bool has_count(false);
     787           0 :         if(exists)
     788             :         {
     789           0 :             has_date = flag.has_parameter("date");
     790           0 :             has_count = flag.has_parameter("count");
     791             :         }
     792             : 
     793             :         // do a first save in case the file did not yet exist
     794             :         //
     795             :         // TODO: find a way to avoid the throw when no .conf exists yet
     796             :         //       it should be as easy as adding  a flag in the snap_config
     797             :         //       which tells the system not to throw and return empty to
     798             :         //       all queries; it's just not done yet
     799             :         //
     800           0 :         flag.save(false);
     801             : 
     802           0 :         std::string const now(std::to_string(time(nullptr)));
     803             : 
     804             :         // setup all the fields as required
     805             :         // (note that setting up the first one will read the file if it exists...)
     806             :         //
     807           0 :         flag["unit"]        = f_unit;
     808           0 :         flag["section"]     = f_section;
     809           0 :         flag["name"]        = f_name;
     810           0 :         flag["source_file"] = f_source_file;
     811           0 :         flag["function"]    = f_function;
     812           0 :         flag["line"]        = std::to_string(f_line);
     813           0 :         flag["message"]     = f_message;
     814           0 :         flag["priority"]    = std::to_string(f_priority);
     815           0 :         flag["manual_down"] = f_manual_down ? "yes" : "no";
     816           0 :         if(!has_date)
     817             :         {
     818           0 :             flag["date"]    = now;
     819             :         }
     820           0 :         flag["modified"]    = now;
     821           0 :         flag["tags"]        = boost::algorithm::join(f_tags, ",");
     822           0 :         flag["hostname"]    = snap::server::get_server_name();
     823           0 :         flag["version"]     = SNAPWEBSITES_VERSION_STRING;
     824             : 
     825           0 :         if(has_count)
     826             :         {
     827             :             // increment existing counter by 1
     828             :             //
     829           0 :             flag["count"] = std::to_string(std::stol(flag["count"]) + 1);
     830             :         }
     831             :         else
     832             :         {
     833           0 :             flag["count"] = "1";
     834             :         }
     835             : 
     836             :         // now save that data to file
     837             :         //
     838           0 :         result = flag.save(false);
     839             :     }
     840             :     else
     841             :     {
     842             :         // state is down, delete the file if it still exists
     843             :         //
     844           0 :         result = unlink(get_filename().c_str()) == 0;
     845           0 :         if(!result && errno == ENOENT)
     846             :         {
     847             :             // deleting a flag that does not exist "works" every time
     848             :             //
     849           0 :             result = true;
     850             :         }
     851             :     }
     852             : 
     853           0 :     return result;
     854             : }
     855             : 
     856             : 
     857             : 
     858             : /** \brief Load all the flag files.
     859             :  *
     860             :  * This function is used to load all the flag files from disk.
     861             :  *
     862             :  * \note
     863             :  * It is expected that the number of flags is always going to be relatively
     864             :  * small. The function make sure that if more than 100 are defined, only
     865             :  * the first 100 are read and another is created warning about the large
     866             :  * number of available flags.
     867             :  *
     868             :  * \return The vector of flag files read from disk.
     869             :  */
     870           0 : snap_flag::vector_t snap_flag::load_flags()
     871             : {
     872             :     // get the path to read with glob_dir
     873             :     //
     874           0 :     snap_config server_config("snapserver");
     875           0 :     std::string path("/var/lib/snapwebsites/flags");
     876           0 :     if(server_config.has_parameter("flag_path"))
     877             :     {
     878           0 :         path = server_config["flag_path"];
     879             :     }
     880             : 
     881             :     // read the list of files
     882             :     //
     883           0 :     glob_dir const flag_filenames(path + "/*.flag", GLOB_NOSORT | GLOB_NOESCAPE, true);
     884           0 :     snap_flag::vector_t result;
     885             :     try
     886             :     {
     887           0 :         flag_filenames.enumerate_glob(std::bind(&load_flag, std::placeholders::_1, &result));
     888             :     }
     889           0 :     catch(flags_exception_too_many_flags const &)
     890             :     {
     891             :         // that error means we have over 100 flags raised
     892             :         //
     893             :         // we raise a "dynamic" flag about this error and ignore the
     894             :         // additional entries in the directory (the ignore happens because
     895             :         // of the throw in the load_flag() function.)
     896             :         //
     897           0 :         auto flag(SNAP_FLAG_UP("snap_flag"
     898             :                              , "flag"
     899             :                              , "too-many-flags"
     900             :                              , "too many flag were raised, showing only the first 100,"
     901           0 :                                " others can be viewed on this system at \"" + path + "\""));
     902           0 :         flag->set_priority(97);
     903           0 :         flag->add_tag("flag");
     904           0 :         flag->add_tag("too-many");
     905           0 :         result.push_back(flag);
     906             :     }
     907             : 
     908           0 :     return result;
     909             : }
     910             : 
     911             : 
     912             : /** \brief Callback function that receives each flag filename.
     913             :  *
     914             :  * When we use the load_flags() function, it calls this callback for
     915             :  * each filename found in the flag_path directory and ending
     916             :  * with the ".flag" extension.
     917             :  *
     918             :  * \param[in] filename  The name of the flag file found in the flag_path
     919             :  *                      directory.
     920             :  * \param[in,out] result  The vector where the new snap_flag object is
     921             :  *                        pushed.
     922             :  */
     923           0 : void snap_flag::load_flag(std::string const & filename, snap_flag::vector_t * result)
     924             : {
     925           0 :     if(result->size() >= 100)
     926             :     {
     927           0 :         throw flags_exception_too_many_flags("too many flags");
     928             :     }
     929             : 
     930           0 :     result->push_back(std::make_shared<snap_flag>(filename));
     931           0 : }
     932             : 
     933             : 
     934             : /** \brief Validate a name so we make sure they are as expected.
     935             :  *
     936             :  * Verify that the name is composed of letters (a-z, A-Z), digits (0-9),
     937             :  * and dashes (-) only.
     938             :  *
     939             :  * Also, it doesn't accept names that start with a digit or a dash.
     940             :  *
     941             :  * Note that the input is read/write because any upper case letters
     942             :  * will be transformed to lowercase (A-Z become a-z).
     943             :  *
     944             :  * Further, the name cannot have two dashes in a row nor a dash at
     945             :  * the end of the name.
     946             :  *
     947             :  * Finally, an empty name is also considered invalid.
     948             :  *
     949             :  * \param[in] name  The name to be checked.
     950             :  */
     951           0 : void snap_flag::valid_name(std::string & name)
     952             : {
     953           0 :     if(name.empty())
     954             :     {
     955           0 :         throw flags_exception_invalid_parameter("unit, section, name, tags cannot be empty");
     956             :     }
     957             : 
     958           0 :     bool first(true);
     959           0 :     char last_char('\0');
     960           0 :     std::string::size_type const max(name.length());
     961           0 :     for(std::string::size_type idx(0); idx < max; ++idx)
     962             :     {
     963           0 :         char c(name[idx]);
     964           0 :         switch(c)
     965             :         {
     966           0 :         case '-':
     967           0 :             if(first)
     968             :             {
     969           0 :                 throw flags_exception_invalid_parameter("unit, section, name, tags cannot start with a dash (-)");
     970             :             }
     971           0 :             if(last_char == '-')
     972             :             {
     973           0 :                 throw flags_exception_invalid_parameter("unit, section, name, tags cannot have two dashes (--) in a row");
     974             :             }
     975           0 :             break;
     976             : 
     977           0 :         case '0':
     978             :         case '1':
     979             :         case '2':
     980             :         case '3':
     981             :         case '4':
     982             :         case '5':
     983             :         case '6':
     984             :         case '7':
     985             :         case '8':
     986             :         case '9':
     987           0 :             if(first)
     988             :             {
     989           0 :                 throw flags_exception_invalid_parameter("unit, section, name, tags cannot start with a digit (-)");
     990             :             }
     991           0 :             break;
     992             : 
     993           0 :         default:
     994           0 :             if(c >= 'A' && c <= 'Z')
     995             :             {
     996             :                 // force all to lowercase
     997             :                 //
     998           0 :                 c |= 0x20;
     999           0 :                 name[idx] = c;
    1000             :             }
    1001           0 :             else if(c < 'a' || c > 'z')
    1002             :             {
    1003           0 :                 throw flags_exception_invalid_parameter("name cannot include characters other than a-z, 0-9, and dashes (-)");
    1004             :             }
    1005           0 :             break;
    1006             : 
    1007             :         }
    1008           0 :         first = false;
    1009           0 :         last_char = c;
    1010             :     }
    1011             : 
    1012           0 :     if(last_char == '-')
    1013             :     {
    1014           0 :         throw flags_exception_invalid_parameter("unit, section, name, tags cannot end with a dash (-)");
    1015             :     }
    1016           0 : }
    1017             : 
    1018             : 
    1019             : 
    1020             : 
    1021             : 
    1022           6 : }
    1023             : // snap namespace
    1024             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13