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
|