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
|