Line data Source code
1 : // Snap Websites Server -- snap websites server
2 : // Copyright (c) 2011-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 "snapwebsites/snapwebsites.h"
25 :
26 :
27 : // snapwebsites lib
28 : //
29 : #include "snapwebsites/log.h"
30 : #include "snapwebsites/snap_backend.h"
31 : #include "snapwebsites/snap_cassandra.h"
32 : #include "snapwebsites/snap_lock.h"
33 : #include "snapwebsites/snap_tables.h"
34 :
35 :
36 : // snapdev lib
37 : //
38 : #include <snapdev/not_used.h>
39 :
40 :
41 : // Qt lib
42 : //
43 : #include <QFile>
44 : #include <QDirIterator>
45 : #include <QHostAddress>
46 : #include <QCoreApplication>
47 : #include <QTextCodec>
48 :
49 :
50 : // C++ lib
51 : //
52 : #include <sstream>
53 :
54 :
55 : // C lib
56 : //
57 : #include <errno.h>
58 : #include <signal.h>
59 : #include <syslog.h>
60 : #include <sys/resource.h>
61 : #include <sys/stat.h>
62 : #include <sys/types.h>
63 :
64 :
65 : // last include
66 : //
67 : #include <snapdev/poison.h>
68 :
69 :
70 :
71 :
72 :
73 : /** \file
74 : * \brief This file represents the Snap! Server.
75 : *
76 : * The snapwebsites.cpp and corresponding header file represents the Snap!
77 : * Server. When you create a server object, its code is available here.
78 : * The server can listen for client connections or run backend processes.
79 : */
80 :
81 :
82 : /** \mainpage
83 : * \brief Snap! C++ Documentation
84 : *
85 : * \section introduction Introduction
86 : *
87 : * The Snap! C++ environment includes a library, plugins, tools, and
88 : * the necessary executables to run the snap server: a fast C++
89 : * CMS (Content Management System).
90 : *
91 : * \section database The Database Environment in Snap! C++
92 : *
93 : * The database makes use of a Cassandra cluster. It is accessed using
94 : * the libQtCassandra class.
95 : *
96 : * \section todo_xxx_tbd Usage of TODO, XXX, and TBD
97 : *
98 : * The TODO mark within the code is used to talk about things that are
99 : * necessary but not yet implemented. The further we progress the less
100 : * of these we should see as we implement each one of them as required.
101 : *
102 : * The XXX mark within the code are things that should be done, although
103 : * it is most generally linked with a question: is it really necessary?
104 : * It can also be a question about the hard coded value (is 5 minutes
105 : * the right amount of time to wait between random session changes?)
106 : * In most cases these should disappear as we get the answer to the
107 : * questions. In effect they are between the TODO and the TBD.
108 : *
109 : * The TBD mark is a pure question: Is that code valid? A TBD does not
110 : * mean that the code needs change just that we cannot really decide,
111 : * at the time it get written, whether it is correct or not. With time
112 : * (especially in terms of usage) we should be able to answer the
113 : * question and transform the question in a comment explaining why
114 : * the code is one way or the other. Of course, if proven wrong, the
115 : * code is to be changed to better fit the needs.
116 : */
117 :
118 :
119 : /** \brief The snap namespace.
120 : *
121 : * The snap namespace is used throughout all the snap objects: libraries,
122 : * plugins, tools.
123 : *
124 : * Plugins make use of a sub-namespace within the snap namespace.
125 : */
126 : namespace snap
127 : {
128 :
129 :
130 : /** \brief Get a fixed name.
131 : *
132 : * The Snap! Server makes use of a certain number of fixed names
133 : * which instead of being defined in macros are defined here as
134 : * static strings. To retrieve one of the strings, call the function
135 : * with the appropriate index.
136 : *
137 : * \param[in] name The name to retrieve.
138 : *
139 : * \return A pointer to the name.
140 : */
141 0 : char const * get_name(name_t name)
142 : {
143 0 : switch(name)
144 : {
145 : // Names that are really considered low level
146 0 : case name_t::SNAP_NAME_SERVER:
147 0 : return "Snap! Server";
148 :
149 0 : case name_t::SNAP_NAME_CONTEXT:
150 0 : return "snap_websites";
151 :
152 0 : case name_t::SNAP_NAME_INDEX: // name used for the domains and websites indexes
153 0 : return "*index*"; // this is a row name inside the domains/websites tables
154 :
155 0 : case name_t::SNAP_NAME_DOMAINS: // domain/sub-domain canonicalization
156 0 : return "domains";
157 :
158 0 : case name_t::SNAP_NAME_WEBSITES: // remaining of URL canonicalization
159 0 : return "websites";
160 :
161 0 : case name_t::SNAP_NAME_SITES: // website global settings
162 0 : return "sites";
163 :
164 0 : case name_t::SNAP_NAME_BACKEND: // backend progress
165 0 : return "backend";
166 :
167 0 : case name_t::SNAP_NAME_MX: // backend progress
168 0 : return "mx";
169 :
170 : // names used by CORE (server and snap_child)
171 0 : case name_t::SNAP_NAME_CORE_ADMINISTRATOR_EMAIL:
172 0 : return "core::administrator_email";
173 :
174 0 : case name_t::SNAP_NAME_CORE_CANONICAL_DOMAIN: // this is only for test websites so search engines know to search on the real site instead
175 0 : return "core::canonical_domain";
176 :
177 0 : case name_t::SNAP_NAME_CORE_CONTENT_DISPOSITION:
178 0 : return "Content-Disposition";
179 :
180 0 : case name_t::SNAP_NAME_CORE_CONTENT_LANGUAGE:
181 0 : return "Content-Language";
182 :
183 0 : case name_t::SNAP_NAME_CORE_CONTENT_TYPE_HEADER:
184 0 : return "Content-Type";
185 :
186 0 : case name_t::SNAP_NAME_CORE_COOKIE_DOMAIN:
187 0 : return "core::cookie_domain";
188 :
189 0 : case name_t::SNAP_NAME_CORE_DATA_PATH:
190 0 : return "data_path";
191 :
192 0 : case name_t::SNAP_NAME_CORE_DATE:
193 0 : return "Date";
194 :
195 0 : case name_t::SNAP_NAME_CORE_EMAIL_CONTENT_ENCODING_QUOTED_PRINTABLE:
196 0 : return "quoted-printable";
197 :
198 0 : case name_t::SNAP_NAME_CORE_EMAIL_CONTENT_TRANSFER_ENCODING:
199 0 : return "Content-Transfer-Encoding";
200 :
201 0 : case name_t::SNAP_NAME_CORE_EMAIL_FROM:
202 0 : return "From";
203 :
204 0 : case name_t::SNAP_NAME_CORE_EMAIL_IMPORTANCE:
205 0 : return "Importance";
206 :
207 0 : case name_t::SNAP_NAME_CORE_EMAIL_LIST_UNSUBSCRIBE:
208 0 : return "List-Unsubscribe";
209 :
210 0 : case name_t::SNAP_NAME_CORE_EMAIL_MESSAGE_ID:
211 0 : return "Message-ID";
212 :
213 0 : case name_t::SNAP_NAME_CORE_EMAIL_MIME_VERSION:
214 0 : return "MIME-Version";
215 :
216 0 : case name_t::SNAP_NAME_CORE_EMAIL_PRECEDENCE:
217 0 : return "Precedence";
218 :
219 0 : case name_t::SNAP_NAME_CORE_EMAIL_PRIORITY_BULK:
220 0 : return "Bulk";
221 :
222 0 : case name_t::SNAP_NAME_CORE_EMAIL_PRIORITY_HIGH:
223 0 : return "High";
224 :
225 0 : case name_t::SNAP_NAME_CORE_EMAIL_PRIORITY_LOW:
226 0 : return "Low";
227 :
228 0 : case name_t::SNAP_NAME_CORE_EMAIL_PRIORITY_NORMAL:
229 0 : return "Normal";
230 :
231 0 : case name_t::SNAP_NAME_CORE_EMAIL_PRIORITY_URGENT:
232 0 : return "Urgent";
233 :
234 0 : case name_t::SNAP_NAME_CORE_EMAIL_REPLY_TO:
235 0 : return "Reply-To";
236 :
237 0 : case name_t::SNAP_NAME_CORE_EMAIL_SUBJECT:
238 0 : return "Subject";
239 :
240 0 : case name_t::SNAP_NAME_CORE_EMAIL_TO:
241 0 : return "To";
242 :
243 0 : case name_t::SNAP_NAME_CORE_EMAIL_X_PRIORITY:
244 0 : return "X-Priority";
245 :
246 0 : case name_t::SNAP_NAME_CORE_EMAIL_X_MSMAIL_PRIORITY:
247 0 : return "X-MSMail-Priority";
248 :
249 0 : case name_t::SNAP_NAME_CORE_HTTP_LINK_HEADER:
250 0 : return "Link";
251 :
252 0 : case name_t::SNAP_NAME_CORE_HTTP_ACCEPT_LANGUAGE:
253 0 : return "HTTP_ACCEPT_LANGUAGE";
254 :
255 0 : case name_t::SNAP_NAME_CORE_HTTP_USER_AGENT:
256 0 : return "HTTP_USER_AGENT";
257 :
258 0 : case name_t::SNAP_NAME_CORE_LAST_DYNAMIC_UPDATE:
259 0 : return "core::last_dynamic_update";
260 :
261 0 : case name_t::SNAP_NAME_CORE_LAST_UPDATED:
262 0 : return "core::last_updated";
263 :
264 0 : case name_t::SNAP_NAME_CORE_LIST_DATA_PATH:
265 0 : return "list_data_path";
266 :
267 0 : case name_t::SNAP_NAME_CORE_LIST_DB_PATH: // sub-path to access the database handled by the pagelist backend ($list_data_path + "/" + "db")
268 0 : return "db";
269 :
270 0 : case name_t::SNAP_NAME_CORE_LIST_JOURNAL_PATH: // sub-path to access the journal generated by the list plugin ($list_data_path + "/" + "journal")
271 0 : return "journal";
272 :
273 0 : case name_t::SNAP_NAME_CORE_LOCATION_HEADER:
274 0 : return "Location";
275 :
276 0 : case name_t::SNAP_NAME_CORE_MX_LAST_CHECKED:
277 0 : return "core::mx_last_checked";
278 :
279 0 : case name_t::SNAP_NAME_CORE_MX_RESULT:
280 0 : return "core::mx_result";
281 :
282 0 : case name_t::SNAP_NAME_CORE_ORIGINAL_RULES:
283 0 : return "core::original_rules";
284 :
285 0 : case name_t::SNAP_NAME_CORE_PARAM_DEFAULT_PLUGINS:
286 0 : return "default_plugins";
287 :
288 0 : case name_t::SNAP_NAME_CORE_PARAM_PLUGINS:
289 0 : return "plugins";
290 :
291 0 : case name_t::SNAP_NAME_CORE_PARAM_PLUGINS_PATH:
292 0 : return "plugins_path";
293 :
294 0 : case name_t::SNAP_NAME_CORE_PARAM_TABLE_SCHEMA_PATH:
295 0 : return "table_schema_path";
296 :
297 0 : case name_t::SNAP_NAME_CORE_PLUGINS:
298 0 : return "core::plugins";
299 :
300 0 : case name_t::SNAP_NAME_CORE_PLUGIN_THRESHOLD:
301 0 : return "core::plugin_threshold";
302 :
303 0 : case name_t::SNAP_NAME_CORE_REDIRECT:
304 0 : return "core::redirect";
305 :
306 0 : case name_t::SNAP_NAME_CORE_REMOTE_ADDR:
307 0 : return "REMOTE_ADDR";
308 :
309 0 : case name_t::SNAP_NAME_CORE_REQUEST_METHOD:
310 0 : return "REQUEST_METHOD";
311 :
312 0 : case name_t::SNAP_NAME_CORE_REQUEST_URI:
313 0 : return "REQUEST_URI";
314 :
315 0 : case name_t::SNAP_NAME_CORE_RETRY_AFTER_HEADER:
316 0 : return "Retry-After";
317 :
318 0 : case name_t::SNAP_NAME_CORE_RULES:
319 0 : return "core::rules";
320 :
321 0 : case name_t::SNAP_NAME_CORE_SERVER_PROTOCOL:
322 0 : return "SERVER_PROTOCOL";
323 :
324 0 : case name_t::SNAP_NAME_CORE_SITE_LONG_NAME:
325 0 : return "core::site_long_name";
326 :
327 0 : case name_t::SNAP_NAME_CORE_SITE_NAME:
328 0 : return "core::site_name";
329 :
330 0 : case name_t::SNAP_NAME_CORE_SITE_READY:
331 0 : return "core::site_ready";
332 :
333 0 : case name_t::SNAP_NAME_CORE_SITE_SECURE:
334 0 : return "core::site_secure";
335 :
336 0 : case name_t::SNAP_NAME_CORE_SITE_SHORT_NAME:
337 0 : return "core::site_short_name";
338 :
339 0 : case name_t::SNAP_NAME_CORE_SITE_STATE:
340 0 : return "core::site_state";
341 :
342 0 : case name_t::SNAP_NAME_CORE_SNAPBACKEND:
343 0 : return "snapbackend";
344 :
345 0 : case name_t::SNAP_NAME_CORE_STATUS_HEADER:
346 0 : return "Status";
347 :
348 0 : case name_t::SNAP_NAME_CORE_TEST_SITE:
349 0 : return "core::test_site";
350 :
351 0 : case name_t::SNAP_NAME_CORE_USER_COOKIE_NAME:
352 0 : return "core::user_cookie_name";
353 :
354 0 : case name_t::SNAP_NAME_CORE_X_POWERED_BY_HEADER:
355 0 : return "X-Powered-By";
356 :
357 0 : default:
358 : // invalid index
359 0 : throw snap_logic_exception(QString("invalid name_t::SNAP_NAME_CORE_... (%1)").arg(static_cast<int>(name)));
360 :
361 : }
362 : NOTREACHED();
363 : }
364 :
365 :
366 : // definitions from the plugins so we can define the name and filename of
367 : // the server plugin
368 : namespace plugins
369 : {
370 : extern QString g_next_register_name;
371 : extern QString g_next_register_filename;
372 : }
373 :
374 :
375 : /** \brief Hidden Snap! Server namespace.
376 : *
377 : * This namespace encompasses global variables only available to the
378 : * server code.
379 : */
380 : namespace
381 : {
382 :
383 :
384 : /** \brief Command line options.
385 : *
386 : * This table includes all the options supported by the server.
387 : */
388 : advgetopt::option const g_snapserver_options[] =
389 : {
390 : advgetopt::define_option(
391 : advgetopt::Name("action")
392 : , advgetopt::ShortName('a')
393 : , advgetopt::Flags(advgetopt::any_flags<advgetopt::GETOPT_FLAG_COMMAND_LINE
394 : , advgetopt::GETOPT_FLAG_ENVIRONMENT_VARIABLE
395 : , advgetopt::GETOPT_FLAG_REQUIRED>())
396 : , advgetopt::Help("Specify a server action.")
397 : ),
398 : advgetopt::define_option(
399 : advgetopt::Name("background")
400 : , advgetopt::ShortName('b')
401 : , advgetopt::Flags(advgetopt::option_flags<advgetopt::GETOPT_FLAG_COMMAND_LINE
402 : , advgetopt::GETOPT_FLAG_ENVIRONMENT_VARIABLE>())
403 : , advgetopt::Help("Detaches the server to the background (default is stay in the foreground.)")
404 : ),
405 : advgetopt::define_option(
406 : advgetopt::Name("config")
407 : , advgetopt::ShortName('c')
408 : , advgetopt::Flags(advgetopt::any_flags<advgetopt::GETOPT_FLAG_COMMAND_LINE
409 : , advgetopt::GETOPT_FLAG_ENVIRONMENT_VARIABLE
410 : , advgetopt::GETOPT_FLAG_REQUIRED
411 : , advgetopt::GETOPT_FLAG_SHOW_USAGE_ON_ERROR>())
412 : , advgetopt::Help("Specify the configuration file to load at startup.")
413 : ),
414 : advgetopt::define_option(
415 : advgetopt::Name("cron-action")
416 : , advgetopt::Flags(advgetopt::any_flags<advgetopt::GETOPT_FLAG_COMMAND_LINE
417 : , advgetopt::GETOPT_FLAG_ENVIRONMENT_VARIABLE
418 : , advgetopt::GETOPT_FLAG_REQUIRED>())
419 : , advgetopt::Help("Specify a server CRON action.")
420 : ),
421 : #ifdef SNAP_NO_FORK
422 : advgetopt::define_option(
423 : advgetopt::Name("nofork")
424 : , advgetopt::ShortName('k')
425 : , advgetopt::Flags(advgetopt::option_flags<advgetopt::GETOPT_FLAG_COMMAND_LINE
426 : , advgetopt::GETOPT_FLAG_ENVIRONMENT_VARIABLE>())
427 : , advgetopt::Help("If set, this switch causes the server not to fork when a child is launched. This should never be use for a production server!")
428 : ),
429 : #endif
430 : advgetopt::define_option(
431 : advgetopt::Name("param")
432 : , advgetopt::ShortName('p')
433 : , advgetopt::Flags(advgetopt::any_flags<advgetopt::GETOPT_FLAG_COMMAND_LINE
434 : , advgetopt::GETOPT_FLAG_ENVIRONMENT_VARIABLE
435 : , advgetopt::GETOPT_FLAG_REQUIRED
436 : , advgetopt::GETOPT_FLAG_MULTIPLE>())
437 : , advgetopt::Help("Define one or more server parameters on the command line (-p name=value).")
438 : ),
439 : advgetopt::define_option(
440 : advgetopt::Name("filename")
441 : , advgetopt::Flags(advgetopt::command_flags<advgetopt::GETOPT_FLAG_DEFAULT_OPTION
442 : , advgetopt::GETOPT_FLAG_REQUIRED
443 : , advgetopt::GETOPT_FLAG_MULTIPLE>())
444 : ),
445 :
446 : // LOG SPECIFIC (moving to snaplogger soon)
447 : //
448 : advgetopt::define_option(
449 : advgetopt::Name("debug")
450 : , advgetopt::ShortName('d')
451 : , advgetopt::Flags(advgetopt::option_flags<advgetopt::GETOPT_FLAG_COMMAND_LINE
452 : , advgetopt::GETOPT_FLAG_ENVIRONMENT_VARIABLE>())
453 : , advgetopt::Help("Outputs debug logs. Perform additional checks in various places.")
454 : ),
455 : advgetopt::define_option(
456 : advgetopt::Name("logfile")
457 : , advgetopt::ShortName('f')
458 : , advgetopt::Flags(advgetopt::any_flags<advgetopt::GETOPT_FLAG_COMMAND_LINE
459 : , advgetopt::GETOPT_FLAG_ENVIRONMENT_VARIABLE
460 : , advgetopt::GETOPT_FLAG_REQUIRED>())
461 : , advgetopt::Help("Output log file to write to. Overrides the setting in the configuration file.")
462 : ),
463 : advgetopt::define_option(
464 : advgetopt::Name("logconf")
465 : , advgetopt::ShortName('l')
466 : , advgetopt::Flags(advgetopt::any_flags<advgetopt::GETOPT_FLAG_COMMAND_LINE
467 : , advgetopt::GETOPT_FLAG_ENVIRONMENT_VARIABLE
468 : , advgetopt::GETOPT_FLAG_REQUIRED>())
469 : , advgetopt::Help("Log configuration file to read from. Overrides log_config in the configuration file.")
470 : ),
471 : advgetopt::define_option(
472 : advgetopt::Name("no-log")
473 : , advgetopt::ShortName('n')
474 : , advgetopt::Flags(advgetopt::option_flags<advgetopt::GETOPT_FLAG_COMMAND_LINE
475 : , advgetopt::GETOPT_FLAG_ENVIRONMENT_VARIABLE>())
476 : , advgetopt::Help("Don't create a logfile, just output to the console.")
477 : ),
478 : advgetopt::define_option(
479 : advgetopt::Name("no-messenger-logging")
480 : , advgetopt::Flags(advgetopt::option_flags<advgetopt::GETOPT_FLAG_COMMAND_LINE>())
481 : , advgetopt::Help("Turn off the automatic logging through snapcommunicator.")
482 : ),
483 :
484 : advgetopt::end_options()
485 : };
486 :
487 :
488 : #pragma GCC diagnostic push
489 : #pragma GCC diagnostic ignored "-Wpedantic"
490 : advgetopt::options_environment g_snapserver_options_environment =
491 : {
492 : .f_project_name = "snapwebsites", // this does NOT vary depending on your program, all writable files are under snapwebsites.d/...
493 : .f_options = g_snapserver_options,
494 : .f_options_files_directory = nullptr,
495 : .f_environment_variable_name = "SNAPSERVER_OPTIONS",
496 : .f_configuration_files = nullptr,
497 : .f_configuration_filename = nullptr,
498 : .f_configuration_directories = nullptr,
499 : .f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_SYSTEM_PARAMETERS,
500 : .f_help_header = "Usage: %p [-<opt>]\n"
501 : "where -<opt> is one or more of:",
502 : .f_help_footer = "Configuration files:\n" // TODO: fix the snapserver::usage() function so this works as expected
503 : "%*f\n"
504 : "Environment variable: %e\n"
505 : "%p v%v from %a\n"
506 : "Built on %t\n"
507 : "See also http://www.snapwebsites.org/project",
508 : .f_version = SNAPWEBSITES_VERSION_STRING,
509 : .f_license = "This software is licenced under the GPL v2 and LGPL v2",
510 : .f_copyright = "Copyright (c) 2011-" BOOST_PP_STRINGIZE(UTC_BUILD_YEAR) " by Made to Order Software Corporation",
511 : //.f_build_date = UTC_BUILD_DATE,
512 : //.f_build_time = UTC_BUILD_TIME
513 : };
514 : #pragma GCC diagnostic pop
515 :
516 0 : struct connection_t
517 : {
518 : snap_communicator::pointer_t f_communicator = snap_communicator::pointer_t();
519 : snap_communicator::snap_connection::pointer_t f_interrupt = snap_communicator::snap_connection::pointer_t();
520 : snap_communicator::snap_connection::pointer_t f_listener = snap_communicator::snap_connection::pointer_t();
521 : snap_communicator::snap_connection::pointer_t f_child_death_listener = snap_communicator::snap_connection::pointer_t();
522 : snap_communicator::snap_connection::pointer_t f_messenger = snap_communicator::snap_connection::pointer_t();
523 : snap_communicator::snap_connection::pointer_t f_cassandra_check_timer = snap_communicator::snap_connection::pointer_t(); // timer in case an error occurs that will not generate a CASSANDRAREADY
524 : };
525 :
526 : /** \brief The pointers to communicator elements.
527 : *
528 : * The communicator we use to run the server events.
529 : *
530 : * \todo
531 : * At some point we need to look into whether it would be possible
532 : * for us to use a shared pointer. At this point the g_connection
533 : * gets allocated and never deleted (not a big deal since it is
534 : * ONE instance for the entire time the process is running.)
535 : */
536 : connection_t * g_connection = nullptr;
537 :
538 :
539 : }
540 : //namespace
541 :
542 :
543 : //#pragma message "Why do we even have this? Adding a smart pointer causes a crash when the server detaches, so commented out."
544 : // Note: We need the argc/argv when we create the application and those are
545 : // not available when we create the server (they are not passed along)
546 : // but I suppose the server could be ameliorated for that purpose...
547 2 : QPointer<QCoreApplication> g_application;
548 :
549 :
550 : /** \brief Server instance.
551 : *
552 : * The g_instance variable holds the current server instance.
553 : */
554 2 : std::shared_ptr<server> server::g_instance;
555 :
556 :
557 : /** \brief Return the server version.
558 : *
559 : * This function can be used to verify that the server version is
560 : * compatible with your plugin or to display the version.
561 : *
562 : * To compare versions, however, it is suggested that you make
563 : * use of the version_major(), version_minor(), and version_patch()
564 : * instead.
565 : *
566 : * \return A pointer to a constant string representing the server version.
567 : */
568 0 : char const * server::version()
569 : {
570 0 : return SNAPWEBSITES_VERSION_STRING;
571 : }
572 :
573 :
574 : /** \brief Return the server major version.
575 : *
576 : * This function returns the major version of the server. This can be used
577 : * to verify that you have the correct version of the server to run your
578 : * plugin.
579 : *
580 : * This is a positive number.
581 : *
582 : * \return The server major version as an integer.
583 : */
584 0 : int server::version_major()
585 : {
586 0 : return SNAPWEBSITES_VERSION_MAJOR;
587 : }
588 :
589 :
590 : /** \brief Return the server minor version.
591 : *
592 : * This function returns the minor version of the server. This can be used
593 : * to verify that you have the correct version of the server to run your
594 : * plugin.
595 : *
596 : * This is a positive number.
597 : *
598 : * \return The server minor version as an integer.
599 : */
600 0 : int server::version_minor()
601 : {
602 0 : return SNAPWEBSITES_VERSION_MINOR;
603 : }
604 :
605 :
606 : /** \brief Return the server patch version.
607 : *
608 : * This function returns the patch version of the server. This can be used
609 : * to verify that you have the correct version of the server to run your
610 : * plugin.
611 : *
612 : * This is a positive number.
613 : *
614 : * \return The server patch version as an integer.
615 : */
616 0 : int server::version_patch()
617 : {
618 0 : return SNAPWEBSITES_VERSION_PATCH;
619 : }
620 :
621 :
622 : /** \brief Get the server instance.
623 : *
624 : * The main central hub is the server object.
625 : *
626 : * Like all the plugins, there can be only one server instance.
627 : * Because of that, it is made a singleton which means whichever
628 : * plugin that first needs the server can get a pointer to it at
629 : * any time.
630 : *
631 : * \note
632 : * This function is not thread safe.
633 : *
634 : * \return A pointer to the server.
635 : */
636 0 : server::pointer_t server::instance()
637 : {
638 0 : if( !g_instance )
639 : {
640 : // plugins registration make use of those two variables
641 0 : plugins::g_next_register_name = "server";
642 0 : plugins::g_next_register_filename = __FILE__;
643 :
644 0 : g_instance.reset( new server );
645 :
646 0 : plugins::g_next_register_name.clear();
647 0 : plugins::g_next_register_filename.clear();
648 : }
649 0 : return g_instance;
650 : }
651 :
652 :
653 : /** \brief Return the current server pointer.
654 : *
655 : * When deriving from the snap server, you cannot put the pointer in
656 : * another variable than the g_instance pointer. However, you cannot
657 : * allocate the right type of server if you call the instance()
658 : * function because it does not use a factory model that allows you
659 : * to create any type of server.
660 : *
661 : * Instead, you call this get_instance() function and if it returns
662 : * a pointer, you create your own server and save its pointer in
663 : * the g_instance variable using the set_instance() function.
664 : *
665 : * \code
666 : * pointer_t my_server(get_instance());
667 : * if(!my_server)
668 : * {
669 : * ...
670 : * set_instance(new my_server_class);
671 : * ...
672 : * }
673 : * \endcode
674 : *
675 : * \return The server instance if defined, may return a null pointer.
676 : */
677 0 : server::pointer_t server::get_instance()
678 : {
679 0 : return g_instance;
680 : }
681 :
682 :
683 : /** \brief When creating a server using a different factory.
684 : *
685 : * This function is used when one create a server using a different
686 : * factory than the main Snap Server factor (i.e. the
687 : * server::instance() function.) For example, the watchdog_server
688 : * uses this function to save a pointer of itself here.
689 : *
690 : * Note that the other server must be derived from the snap::server
691 : * class, obviously.
692 : *
693 : * See the get_instance() for more information about how to allocate
694 : * a new server. As an example, check out the lib/snapwatchdog.cpp file.
695 : *
696 : * \param[in] other_server The other type of server to save in this instance.
697 : *
698 : * \return A pointer to the new instance of the server.
699 : */
700 0 : server::pointer_t server::set_instance(pointer_t other_server)
701 : {
702 0 : if(g_instance)
703 : {
704 0 : throw snap_logic_exception("server::set_instance() cannot be called more than once.");
705 : }
706 :
707 0 : return g_instance = other_server;
708 : }
709 :
710 :
711 : /** \brief A path or URI to a logo for this plugin.
712 : *
713 : * This function returns a 64x64 icons representing this plugin.
714 : *
715 : * \return A path to the logo.
716 : */
717 0 : QString server::icon() const
718 : {
719 0 : return "/images/snap/snap-logo-64x64.png";
720 : }
721 :
722 :
723 : /** \brief Return the description of this plugin.
724 : *
725 : * This function returns the English description of this plugin.
726 : * The system presents that description when the user is offered to
727 : * install or uninstall a plugin on his website. Translation may be
728 : * available in the database.
729 : *
730 : * \return The description in a QString.
731 : */
732 0 : QString server::description() const
733 : {
734 : return "The server plugin is hard coded in the base of the system."
735 : " It handles the incoming and outgoing network connections."
736 0 : " The server handles a number of messages that are global.";
737 : }
738 :
739 :
740 : /** \brief Return our dependencies.
741 : *
742 : * The server has no dependencies so this function returns an empty string.
743 : *
744 : * \return An empty string.
745 : */
746 0 : QString server::dependencies() const
747 : {
748 0 : return QString();
749 : }
750 :
751 :
752 : /** \brief Required bootstrap definition.
753 : *
754 : * This function does nothing as the server object is already properly
755 : * initialized by the time this function gets called.
756 : *
757 : * However, since it is a pure virtual function, we suppose that it
758 : * is required.
759 : */
760 0 : void server::bootstrap(snap_child * snap)
761 : {
762 : // virtual function stub
763 0 : NOTUSED(snap);
764 0 : }
765 :
766 :
767 : /** \brief Update the server, the function is mandatory.
768 : *
769 : * This function is here because it is a pure virtual in the plug in. At this
770 : * time it does nothing and it probably will never have actual updates.
771 : *
772 : * \param[in] last_updated The UTC Unix date when this plugin was last updated (in micro seconds).
773 : *
774 : * \return The UTC Unix date of the last update of this plugin.
775 : */
776 0 : int64_t server::do_update(int64_t last_updated)
777 : {
778 0 : NOTUSED(last_updated);
779 :
780 0 : SNAP_PLUGIN_UPDATE_INIT();
781 0 : SNAP_PLUGIN_UPDATE_EXIT();
782 : }
783 :
784 :
785 : /** \brief Initialize the server.
786 : *
787 : * This function initializes the server.
788 : *
789 : * \note
790 : * The server is also a plugin. This is useful for having support for
791 : * signals in the server.
792 : */
793 0 : server::server()
794 : : f_parameters("snapserver")
795 0 : , f_translator() // forced to include because of -Weffc++
796 : {
797 : // set the plugin version
798 0 : set_version(SNAPWEBSITES_VERSION_MAJOR, SNAPWEBSITES_VERSION_MINOR);
799 0 : }
800 :
801 :
802 : /** \brief Clean up the server.
803 : *
804 : * Since the server is a singleon, it never gets deleted while running.
805 : * Since we use a bare pointer, it should never go out of scope, thus
806 : * this function should never be called.
807 : */
808 0 : server::~server()
809 : {
810 0 : for( auto const & child : f_children_waiting )
811 : {
812 0 : delete child;
813 : }
814 0 : f_children_waiting.clear();
815 : //
816 0 : for( auto const & child : f_children_running )
817 : {
818 0 : if( child )
819 : {
820 0 : child->kill();
821 : }
822 : //
823 0 : delete child;
824 : }
825 0 : f_children_running.clear();
826 :
827 : // Destroy the QApplication instance.
828 : //
829 0 : g_application = nullptr;
830 0 : }
831 :
832 :
833 : /** \brief Exit the server.
834 : *
835 : * This function exists the program by calling the exit(3) function from
836 : * the C library. Before doing so, though, it will first make sure that
837 : * the server is cleaned up as required.
838 : *
839 : * \param[in] code The exit code, generally 0 or 1.
840 : */
841 0 : void server::exit( int const code )
842 : {
843 : // Destroy the snapwebsites server instance.
844 : //
845 0 : g_instance.reset();
846 0 : g_application = nullptr; // Make sure the QApplication instance is really deleted.
847 :
848 : // Call the C exit(3) function.
849 : //
850 0 : ::exit(code);
851 :
852 : // Sanity check!
853 : //
854 : NOTREACHED();
855 : }
856 :
857 :
858 : /** \brief Print out usage information to start the server.
859 : *
860 : * This function prints out a usage message that describes the arguments
861 : * that the server accepts on the command line.
862 : *
863 : * The function calls exit(1) and never returns.
864 : */
865 0 : void server::usage()
866 : {
867 : // TODO: switch to the config. from advgetopt and then we can just
868 : // use a %<flag> such as %f, %*g, etc.
869 : //
870 0 : std::stringstream ss_footer;
871 : ss_footer << "Configuration File: \""
872 0 : << f_parameters.get_configuration_path()
873 : << "/"
874 0 : << f_parameters.get_configuration_filename()
875 0 : << ".conf\""
876 0 : << std::endl;
877 0 : std::string footer(ss_footer.str());
878 0 : g_snapserver_options_environment.f_help_footer = footer.c_str();
879 :
880 0 : std::cout << f_opt->usage();
881 :
882 0 : g_snapserver_options_environment.f_help_footer = nullptr;
883 :
884 0 : exit(1);
885 : }
886 :
887 :
888 : /** \brief Change the configuration filename.
889 : *
890 : * The various daemons that make use of the server will generally want to
891 : * use a different .conf filename (i.e. snapwatchdog uses snapwatchdog.conf
892 : * instead of snapserver.conf). This function is used for that purpose
893 : * right after the server was created, call it with the name of your
894 : * configuration file.
895 : *
896 : * The path is not set here. The default is "/etc/snapwebsites". It can
897 : * be changed using the --config command line option.
898 : *
899 : * \param[in] filename The name of the configuration file.
900 : */
901 0 : void server::set_config_filename(std::string const & filename)
902 : {
903 0 : f_parameters.set_configuration_filename(filename);
904 0 : }
905 :
906 :
907 : /** \brief Mark the server object as a backend tool instead.
908 : *
909 : * This function is called by the backend tool to mark the server
910 : * as a command line tool rather than a server. In general, this
911 : * is ignored, but there are a few cases where it is checked to
912 : * make sure that everything works as expected.
913 : *
914 : * The function can be called as many times as necessary.
915 : */
916 0 : void server::setup_as_backend()
917 : {
918 0 : f_backend = true;
919 0 : }
920 :
921 :
922 : /** \fn server::is_backend() const;
923 : * \brief Check whether the server is setup as a backend.
924 : *
925 : * This function returns false unless the setup_as_backend()
926 : * funciton was called.
927 : *
928 : * \return true if this is a server, false if this is used as a command line tool
929 : */
930 :
931 :
932 : /** \brief Print the version string to stdout.
933 : *
934 : * This function prints out the version string of this server to the standard
935 : * output stream.
936 : *
937 : * This is a virtual function so that way servers and daemons that derive
938 : * from snap::server have a chance to show their own version.
939 : */
940 0 : void server::show_version()
941 : {
942 0 : std::cout << SNAPWEBSITES_VERSION_STRING << std::endl;
943 0 : }
944 :
945 :
946 : /** \brief Configure the server.
947 : *
948 : * This function parses the command line arguments and reads the
949 : * configuration file.
950 : *
951 : * By default, the configuration file is defined as:
952 : *
953 : * \code
954 : * /etc/snapwebsites/snapserver.conf
955 : * \endcode
956 : *
957 : * The user may use the --config argument to use a different file.
958 : *
959 : * The function does not return if any of the arguments generate an
960 : * error or if the configuration file has an invalid parameter.
961 : *
962 : * \note
963 : * In this function we still use syslog() to log errors because the
964 : * logger is initialized at the end of the function once we got
965 : * all the necessary information to initialize the logger. Later we
966 : * may want to record the configuration file errors and log them
967 : * if we can still properly initialize the logger.
968 : *
969 : * \param[in] argc The number of arguments in argv.
970 : * \param[in] argv The array of argument strings.
971 : */
972 0 : void server::config(int argc, char * argv[])
973 : {
974 : // Stop on these signals, log them, then terminate.
975 : //
976 0 : signal( SIGSEGV, sighandler );
977 0 : signal( SIGBUS, sighandler );
978 0 : signal( SIGFPE, sighandler );
979 0 : signal( SIGILL, sighandler );
980 0 : signal( SIGTERM, sighandler );
981 0 : signal( SIGINT, sighandler );
982 0 : signal( SIGQUIT, sighandler );
983 0 : signal( SIGALRM, sighandler );
984 0 : signal( SIGABRT, sighandler ); // although we can't really return from this one, having the stack trace is useful
985 :
986 : // we want to ignore SIGPIPE, but having a log is really useful so
987 : // we use a signal handler that logs the info and returns,
988 : // letting the daemon continue
989 : //
990 0 : signal( SIGPIPE, sigloghandler );
991 :
992 : // ignore console signals
993 : //
994 0 : signal( SIGTSTP, SIG_IGN );
995 0 : signal( SIGTTIN, SIG_IGN );
996 0 : signal( SIGTTOU, SIG_IGN );
997 :
998 : // Force timezone to UTC/GMT so it does not vary between installations
999 : // (i.e. you could have Snap servers all over the world!)
1000 : //
1001 0 : setenv("TZ", "", 1); // default is UTC
1002 0 : tzset();
1003 :
1004 : // Force the locale to "C" so we do not get too many surprises.
1005 : // Users may change their locale settings so a child may change
1006 : // the locale for display formatting needs.
1007 : //
1008 0 : char const * default_locale(std::setlocale(LC_ALL, "C.UTF-8"));
1009 0 : if(default_locale == nullptr)
1010 : {
1011 0 : std::locale const & loc(std::locale("C"));
1012 0 : std::locale::global(loc); // default depends on LC_... vars
1013 0 : std::cin.imbue(loc);
1014 0 : std::cout.imbue(loc);
1015 0 : std::cerr.imbue(loc);
1016 : }
1017 : else
1018 : {
1019 : // if we can use UTF-8, do so rather than just plain C
1020 0 : std::locale const & loc(std::locale("C.UTF-8"));
1021 0 : std::locale::global(loc); // default depends on LC_... vars
1022 0 : std::cin.imbue(loc);
1023 0 : std::cout.imbue(loc);
1024 0 : std::cerr.imbue(loc);
1025 : }
1026 : // TBD: we initialize the Qt library later, I do not think it will
1027 : // change the locale on us, but this is a TBD until otherwise
1028 : // proven to be safe... (see QLocale) -- and that could change
1029 : // when we start using Qt 5.x
1030 :
1031 : // Parse command-line options...
1032 : //
1033 :
1034 0 : f_opt = std::make_shared<advgetopt::getopt>( g_snapserver_options_environment, argc, argv );
1035 :
1036 0 : if(f_opt->is_defined("version"))
1037 : {
1038 0 : show_version();
1039 0 : exit(0);
1040 : NOTREACHED();
1041 : }
1042 :
1043 : // We want the servername for later.
1044 : //
1045 : // TODO: this f_servername is the name of the daemon binary, not
1046 : // the name of the computer; we want to change that variable
1047 : // name and rename the corresponding functions too at some point
1048 : //
1049 0 : f_servername = f_opt->get_program_name();
1050 :
1051 0 : if(f_service_name.empty())
1052 : {
1053 0 : f_service_name = f_servername;
1054 : }
1055 :
1056 : // Keep the server in the foreground?
1057 : //
1058 0 : f_foreground = !f_opt->is_defined( "background" );
1059 :
1060 : // initialize the syslog() interface
1061 0 : openlog(f_servername.c_str(), LOG_NDELAY | LOG_PID, LOG_DAEMON);
1062 :
1063 0 : bool help(false);
1064 :
1065 : // handle configuration file
1066 : //
1067 : // One can change the path with "--config <new path>", but not the
1068 : // filename of the configuration file.
1069 : //
1070 0 : if(f_opt->is_defined("config"))
1071 : {
1072 0 : f_parameters.set_configuration_path(f_opt->get_string("config"));
1073 : }
1074 :
1075 : // default parameters -- we may want to have a separate function and
1076 : // maybe some clear separate variables?
1077 0 : f_parameters.set_parameter_default("listen", "127.0.0.1:4004");
1078 0 : f_parameters.set_parameter_default(get_name(name_t::SNAP_NAME_CORE_PARAM_PLUGINS_PATH), "/usr/lib/snapwebsites/plugins");
1079 0 : f_parameters.set_parameter_default(get_name(name_t::SNAP_NAME_CORE_PARAM_TABLE_SCHEMA_PATH), "/usr/lib/snapwebsites/tables");
1080 0 : f_parameters.set_parameter_default("qs_action", "a");
1081 0 : f_parameters.set_parameter_default("qs_hit", "hit");
1082 :
1083 : // Output log to stdout. Implies foreground mode.
1084 : //
1085 0 : f_debug = f_opt->is_defined( "debug" )
1086 0 : || f_parameters.has_parameter("debug");
1087 :
1088 0 : if(f_opt->is_defined("param"))
1089 : {
1090 0 : int const max_params(f_opt->size("param"));
1091 0 : for(int idx(0); idx < max_params; ++idx)
1092 : {
1093 0 : QString const param(QString::fromUtf8(f_opt->get_string("param", idx).c_str()));
1094 0 : int const p(param.indexOf('='));
1095 0 : if(p == -1)
1096 : {
1097 0 : SNAP_LOG_FATAL("unexpected parameter \"--param ")(f_opt->get_string("param", idx))("\". No '=' found in the parameter definition. (in server::config())");
1098 0 : syslog(LOG_CRIT, "unexpected parameter \"--param %s\". No '=' found in the parameter definition. (in server::config())", f_opt->get_string("param", idx).c_str());
1099 0 : help = true;
1100 : }
1101 : else
1102 : {
1103 : // got a user defined parameter
1104 0 : QString const name(param.left(p));
1105 0 : f_parameters[name] = param.mid(p + 1);
1106 : }
1107 : }
1108 : }
1109 :
1110 0 : if( f_opt->is_defined( "filename" ) )
1111 : {
1112 0 : std::string const filename(f_opt->get_string("filename"));
1113 0 : if( f_backend )
1114 : {
1115 0 : f_parameters["__BACKEND_URI"] = filename.c_str();
1116 : }
1117 : else
1118 : {
1119 : // If not backend, "--filename" is not currently useful.
1120 : //
1121 0 : SNAP_LOG_FATAL("unexpected standalone parameter \"")(filename)("\", server not started. (in server::config())");
1122 0 : syslog( LOG_CRIT, "unexpected standalone parameter \"%s\", server not started. (in server::config())", filename.c_str() );
1123 0 : help = true;
1124 : }
1125 : }
1126 :
1127 0 : if( f_opt->is_defined( "action" ) )
1128 : {
1129 0 : std::string const action(f_opt->get_string("action"));
1130 0 : if( f_backend )
1131 : {
1132 0 : f_parameters["__BACKEND_ACTION"] = action.c_str();
1133 : }
1134 : else
1135 : {
1136 : // If not backend, "--action" does not make sense.
1137 : //
1138 0 : SNAP_LOG_FATAL("unexpected command line option \"--action ")(action)("\", server not started as backend. (in server::config())");
1139 0 : syslog( LOG_CRIT, "unexpected command line option \"--action %s\", server not started as backend. (in server::config())", action.c_str() );
1140 0 : help = true;
1141 : }
1142 0 : if( f_opt->is_defined( "cron-action" ) )
1143 : {
1144 : // --action and --cron-action are mutually exclusive
1145 : //
1146 0 : SNAP_LOG_FATAL("command line options \"--action\" and \"--cron-action\" are mutually exclusive, server not started as backend. (in server::config())");
1147 0 : syslog( LOG_CRIT, "command line options \"--action\" and \"--cron-action\" are mutually exclusive, server not started as backend. (in server::config())" );
1148 0 : help = true;
1149 : }
1150 : }
1151 :
1152 0 : if( f_opt->is_defined( "cron-action" ) )
1153 : {
1154 0 : std::string const cron_action(f_opt->get_string("cron-action"));
1155 0 : if( f_backend )
1156 : {
1157 0 : f_parameters["__BACKEND_CRON_ACTION"] = cron_action.c_str();
1158 : }
1159 : else
1160 : {
1161 : // If not backend, "--cron-action" does not make sense.
1162 : //
1163 0 : SNAP_LOG_FATAL("unexpected command line option \"--cron-action ")(cron_action)("\", server not started as backend. (in server::config())");
1164 0 : syslog( LOG_CRIT, "unexpected command line option \"--cron-action %s\", server not started as backend. (in server::config())", cron_action.c_str() );
1165 0 : help = true;
1166 : }
1167 : }
1168 :
1169 0 : if( help || f_opt->is_defined( "help" ) )
1170 : {
1171 0 : usage();
1172 : exit(1);
1173 : }
1174 :
1175 : // Finally we can initialize the log system
1176 : //
1177 0 : logging::set_progname(f_servername);
1178 0 : if( f_opt->is_defined( "no-log" ) )
1179 : {
1180 : // Override log_config and output only to the console
1181 : //
1182 0 : logging::configure_console();
1183 : }
1184 0 : else if( f_opt->is_defined("logfile") )
1185 : {
1186 : // Override the output logfile specified in the configuration file.
1187 : //
1188 0 : logging::configure_logfile( f_opt->get_string( "logfile" ).c_str() );
1189 : }
1190 0 : else if( f_opt->is_defined("logconf") )
1191 : {
1192 0 : logging::configure_conffile( f_opt->get_string( "logconf" ).c_str() );
1193 : }
1194 : else
1195 : {
1196 : // Read the log configuration file and use it to specify the appenders
1197 : // and log level. If a server version exists and the server is
1198 : // available then use the loggging server.
1199 : //
1200 0 : QString const log_config( f_parameters["log_config"] );
1201 0 : if( log_config.isEmpty() )
1202 : {
1203 : // Fall back to output to the console
1204 : //
1205 0 : logging::configure_console();
1206 : }
1207 : else
1208 : {
1209 : // Configure the logging system according to the log configuration.
1210 : //
1211 0 : logging::configure_conffile( log_config );
1212 : }
1213 : }
1214 :
1215 : #ifdef SNAP_NO_FORK
1216 : SNAP_LOG_WARNING("SNAP_NO_FORK is defined! This is NOT a production-ready build!");
1217 : if( f_opt->is_defined("nofork") )
1218 : {
1219 : SNAP_LOG_INFO("--nofork specified: snap_child will not fork and server will terminate.");
1220 : f_nofork = true;
1221 : }
1222 : #endif
1223 :
1224 0 : if( f_debug )
1225 : {
1226 : // Force the logger level to DEBUG
1227 : // If the current level is already lower, leave it as is
1228 : //
1229 0 : logging::reduce_log_output_level( logging::log_level_t::LOG_LEVEL_DEBUG );
1230 : }
1231 :
1232 0 : QString const lock_obtension_duration( f_parameters["lock_obtension_duration"] );
1233 0 : if( !lock_obtension_duration.isEmpty() )
1234 : {
1235 0 : bool ok(false);
1236 0 : int const lock_obtension(lock_obtension_duration.toInt(&ok, 10));
1237 0 : if(ok)
1238 : {
1239 0 : snap_lock::initialize_lock_obtention_timeout(lock_obtension);
1240 : }
1241 : }
1242 :
1243 : // determine the name of the server
1244 : //
1245 0 : get_server_name();
1246 :
1247 : // determine whether the snapfirewall daemon is active
1248 : // (if so we want to wait for the FIREWALLUP message)
1249 : //
1250 0 : f_firewall_is_active = system("systemctl is-active -q snapfirewall") == 0;
1251 0 : }
1252 :
1253 :
1254 : /** \brief Define the service name.
1255 : *
1256 : * This function sets the service name to the specified parameter. By
1257 : * default the service name is set to the process name. So for example
1258 : * the snapserver service name is "snapserver".
1259 : *
1260 : * This function is useful to change the service name of processes such
1261 : * as the snapbackend processes which all use the same process but perform
1262 : * different services.
1263 : *
1264 : * \param[in] service_name The name of the service using the server class.
1265 : */
1266 0 : void server::set_service_name(std::string const & service_name)
1267 : {
1268 0 : f_service_name = service_name;
1269 0 : }
1270 :
1271 :
1272 : /** \brief Retrieve the service name.
1273 : *
1274 : * This function returns the service name. This is most often the same
1275 : * as the servername() parameter only sometimes the service name gets
1276 : * changed to avoid confusion. For example, snapbackend is used for
1277 : * several different services such as "list::pagelist" and "images::images".
1278 : *
1279 : * \note
1280 : * The service names should not include colons in their names. The
1281 : * snapbackend will transform the action name to use underscores
1282 : * instead (i.e. "list__pagelist" or "images__images".)
1283 : *
1284 : * \note
1285 : * This name is used as the PID filename. It is important for us to be
1286 : * able to distinguish between various snapbackend processes since each
1287 : * PID file needs a distinct filename.
1288 : *
1289 : * \return A reference to the service name.
1290 : */
1291 0 : std::string const & server::get_service_name() const
1292 : {
1293 0 : return f_service_name;
1294 : }
1295 :
1296 :
1297 : /** \brief Get the server name.
1298 : *
1299 : * This function retrieves the name of the server. If it is defined in
1300 : * the snapcommunicator.conf file, then that name is returned. If not
1301 : * defined there, then the hostname() function is used to retrieve
1302 : * the name of the computer.
1303 : *
1304 : * The name will be verified and reformatted to be compatible with
1305 : * the snapcommunicator messaging system. This means '-' are replaced
1306 : * with '_', A to Z are replaced by a to z, and names cannot start
1307 : * with a digit or be empty.
1308 : *
1309 : * \note
1310 : * The function caches the name so it does not have to recalculate
1311 : * it over and over again. This also means that a change of the hostname
1312 : * will not be seen by one of our daemons until it gets restarted.
1313 : *
1314 : * \return A copy of the server name.
1315 : */
1316 0 : std::string server::get_server_name()
1317 : {
1318 : // if called more than once, returned the same name each time after that
1319 : //
1320 0 : static std::string saved_server_name;
1321 0 : if(saved_server_name.empty())
1322 : {
1323 : // WARNING: we create a separate version of the parameters variable,
1324 : // but remember that all the configurations accessible
1325 : // through that interface which saves them in a global, so
1326 : // yes, it is a separate parameter, but really the same
1327 : // configuration variables
1328 : //
1329 : // we expect server_name to only be defined in the
1330 : // snapcommunicator.conf file because it needs it
1331 : // and it gets started first.
1332 : //
1333 : // Note: We create this separate variable because this is a static
1334 : // function and thus we do not have access to f_parameters.
1335 : //
1336 0 : snap_config parameters("snapcommunicator");
1337 :
1338 0 : snap_config::snap_config_parameter_ref server_name(parameters["server_name"]);
1339 :
1340 : // if the parameter was not defined in the configuration file,
1341 : // read the system hostname
1342 : //
1343 0 : if(server_name.empty())
1344 : {
1345 : // use hostname by default if undefined in configuration file
1346 : //
1347 : char host[HOST_NAME_MAX + 1];
1348 0 : host[HOST_NAME_MAX] = '\0';
1349 0 : if(gethostname(host, sizeof(host)) != 0
1350 0 : || strlen(host) == 0)
1351 : {
1352 0 : throw snapwebsites_exception_parameter_not_available("snapwebsites.cpp: server::get_server_name() could not determine the name of this server.");
1353 : }
1354 : // TODO: add code to verify that we like that name (i.e. if the
1355 : // name includes periods we will reject it when sending
1356 : // messages to/from snapcommunicator)
1357 : //
1358 0 : server_name = host;
1359 : }
1360 :
1361 0 : saved_server_name = server_name;
1362 :
1363 0 : verify_server_name(saved_server_name);
1364 : }
1365 :
1366 0 : return saved_server_name;
1367 : }
1368 :
1369 :
1370 : /** \brief Verify a name that is expected to be used as a server name.
1371 : *
1372 : * This function is used to check a \p server_name string. The function
1373 : * fixes up the name (replace the '-' with '_', removed any characters
1374 : * after the first '.', and force characters to lowercase.)
1375 : *
1376 : * The name cannot be empty nor larger than 63 characters. Note that
1377 : * a name that starts with a period looks like it is empty.
1378 : *
1379 : * \param[in,out] server_name The name to be checked.
1380 : */
1381 0 : void server::verify_server_name(std::string & server_name)
1382 : {
1383 0 : std::string name;
1384 0 : std::string const original(server_name);
1385 0 : char const * s(original.c_str());
1386 0 : while(*s != '\0' && *s != '.')
1387 : {
1388 0 : if(*s == '-')
1389 : {
1390 : // the dash is not acceptable in our server name
1391 : // replace it with an underscore
1392 : //
1393 0 : SNAP_LOG_WARNING("Hostname \"")
1394 0 : (std::string(server_name))
1395 0 : ("\" includes a dash character (-) which is not supported by snap."
1396 : " Replacing with an underscore (_). If that is not what you expect,"
1397 : " edit \"/etc/snapwebsites/snapwebsites.d/snapcommunicator.conf\""
1398 : " and set the name as you want it in \"server_name=...\"");
1399 0 : name += '_';
1400 : }
1401 0 : else if(*s >= 'A' && *s <= 'Z')
1402 : {
1403 : // force lowercase -- hostnames are expected to be in
1404 : // lowercase although they are case insensitive so we
1405 : // certainly want them to be in lowercase anyway
1406 : //
1407 : // note: we do not support UTF-8 servernames so really only
1408 : // ASCII will be taken in account here
1409 : //
1410 0 : name += *s | 0x20;
1411 : }
1412 0 : else if((*s >= 'a' && *s <= 'z')
1413 0 : || (*s >= '0' && *s <= '9')
1414 0 : || *s == '_')
1415 : {
1416 0 : name += *s;
1417 : }
1418 : else
1419 : {
1420 0 : throw snapwebsites_exception_invalid_parameters("snapwebsites.cpp: server::get_server_name() found invalid characters in your server_name parameter.");
1421 : }
1422 :
1423 0 : ++s;
1424 : }
1425 0 : if(*s == '.')
1426 : {
1427 : // according to the hostname documentation, the FQDN is
1428 : // the name before the first dot; this means if you have
1429 : // more than two dots, the sub-sub-sub...sub-domain is
1430 : // the FQDN
1431 : //
1432 0 : SNAP_LOG_WARNING("Hostname \"")(std::string(server_name))("\" includes a dot character (.) which is not supported by snap. We assume that indicates the end of the name. If that is not what you expect, edit snapcommunicator.conf and set the name as you want it in server_name=...");
1433 : }
1434 :
1435 :
1436 : // TBD: We could further prevent the name from starting/ending with '_'?
1437 : //
1438 0 : if(server_name != name)
1439 : {
1440 : // warning about changing the name (not that in the above loop
1441 : // we do not warn about changing the name to lowercase)
1442 : //
1443 0 : SNAP_LOG_WARNING("Your server_name parameter \"")(std::string(server_name))("\" was transformed to \"")(name)("\" to be compatible with Snap!");
1444 0 : server_name = name;
1445 : }
1446 :
1447 : // make sure the computer name is no more than 63 characters
1448 : //
1449 0 : if(server_name.empty()
1450 0 : || server_name.length() > 63)
1451 : {
1452 0 : throw snapwebsites_exception_invalid_parameters("snapwebsites.cpp: server::get_server_name(): your server_name parameter is empty or too long. The maximum length is 63 characters.");
1453 : }
1454 :
1455 : // make sure we can use that name to send messages between computers
1456 : //
1457 0 : snap::snap_communicator_message::verify_name(QString::fromUtf8(server_name.c_str()), false, true);
1458 0 : }
1459 :
1460 :
1461 : /** \brief Retrieve the number of threads in this process.
1462 : *
1463 : * This function counts the total number of threads that this process is
1464 : * currently running with.
1465 : *
1466 : * \todo
1467 : * We should make sure that the count is 1 before any call to fork().
1468 : *
1469 : * \return The number of running threads in this process.
1470 : */
1471 0 : size_t server::thread_count()
1472 : {
1473 : struct stat task;
1474 :
1475 0 : if(stat("/proc/self/task", &task) != 0)
1476 : {
1477 0 : return -1;
1478 : }
1479 :
1480 0 : return task.st_nlink - 2;
1481 : }
1482 :
1483 :
1484 : /** \brief Retrieve one of the configuration file parameters.
1485 : *
1486 : * This function returns the value of a named parameter. The
1487 : * parameter is defined in the configuration file, it may also
1488 : * be given a default value when the server is initialized.
1489 : *
1490 : * The following are the parameters currently supported by
1491 : * the core system. Additional parameters may be defined by
1492 : * plugins. Remember that parameters defined in the
1493 : * configuration file are common to ALL the websites and at
1494 : * this point plugins do not have direct access to the
1495 : * get_parameter() function (look at the get_site_parameter()
1496 : * function in the snap_child class as a better alternative
1497 : * for plugins.)
1498 : *
1499 : * \li backend_nice -- the nice value to use with backends; if undefined, keep
1500 : * the default nice value (i.e. 0)
1501 : * \li cassandra_host -- the IP address or server name to Cassandra; default is localhost
1502 : * \li cassandra_port -- the port to use to connect to Cassandra; default is 9042
1503 : * \li data_path -- path to the directory holding the system data (images, js, css, counters, etc.)
1504 : * \li default_plugins -- list of default plugins to initialize a new website
1505 : * \li listen -- address:port to listen to (default 0.0.0.0:4004)
1506 : * \li plugins -- path to the list of plugins
1507 : * \li qs_action -- the variable holding the action over this path ("view" if not specified)
1508 : * \li max_pending_connections -- the number of connections that can wait in
1509 : * the server queue, there is Snap default (i.e. the Qt TCP server default
1510 : * is used if undefined, which in most cases means the system of 5.)
1511 : * \li server_name -- the name of the server, defaults to gethostname()
1512 : * \li timeout_wait_children -- the amount of time to wait before checking on
1513 : * the existing children; cannot be less than 100ms; defaults to 5,000ms
1514 : *
1515 : * \param[in] param_name The name of the parameter to retrieve.
1516 : *
1517 : * \sa set_parameter()
1518 : *
1519 : * \return The value of the specified parameter.
1520 : */
1521 0 : QString server::get_parameter(QString const & param_name) const
1522 : {
1523 0 : return f_parameters[param_name];
1524 : }
1525 :
1526 :
1527 : /** \brief Set one of the configuration file parameters.
1528 : *
1529 : * \param[in] param_name The name of the parameter to retrieve.
1530 : * \param[in] value The value to put into the parameter.
1531 : *
1532 : * \sa get_parameter()
1533 : */
1534 0 : void server::set_parameter( const QString& param_name, const QString& value )
1535 : {
1536 0 : f_parameters[param_name] = value;
1537 0 : }
1538 :
1539 :
1540 : /** \brief Set up the Qt4 application instance.
1541 : *
1542 : * This function creates the Qt4 application instance for application-wide use.
1543 : *
1544 : * \note This is code moved from config() above, since initializing and trying to delete
1545 : * on detach caused a crash.
1546 : */
1547 0 : void server::prepare_qtapp( int argc, char *argv[] )
1548 : {
1549 : // make sure the Qt Locale is UTF-8
1550 0 : QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
1551 :
1552 0 : if(!g_application)
1553 : {
1554 : // We install a translator early, but language files are only
1555 : // loaded if the user is logged in or a website specified a
1556 : // locale which is not "en" or "en_US".
1557 : //
1558 0 : g_application = QPointer<QCoreApplication>( new QCoreApplication(argc, argv) );
1559 0 : g_application->installTranslator(&f_translator);
1560 : }
1561 0 : }
1562 :
1563 :
1564 : /** \brief Change the current translation.
1565 : *
1566 : * This function is called whenever a new translation becomes available.
1567 : * In most cases this happens whenever a user is logged in the system.
1568 : *
1569 : * At some point we may want to provide a translation capability from
1570 : * the server settings so one can have most error messages translated
1571 : * in their main country language instead of the default English.
1572 : *
1573 : * The \p xml_data buffer is XML as output by Qt Linguist. We actually
1574 : * make use of our own translation tool in Snap! and have a backend
1575 : * process which gathers all the translations and generates one XML
1576 : * file for each given language.
1577 : *
1578 : * \param[in] xml_data The XML data representing the translation as
1579 : * expected by QTranslator.
1580 : */
1581 0 : void server::set_translation(QString const xml_data)
1582 : {
1583 : // WARNING: the translation must not disappear when installed from
1584 : // using the load(char const *, int) overload function so
1585 : // we keep a copy in f_translation_xml
1586 : //
1587 0 : f_translation_xml = xml_data.toUtf8();
1588 :
1589 : // we use a cast because .data() returns a 'char *'
1590 0 : f_translator.load(reinterpret_cast<uchar const *>(f_translation_xml.data()), f_translation_xml.size());
1591 0 : }
1592 :
1593 :
1594 : /** \brief Prepare the Cassandra database.
1595 : *
1596 : * This function ensures that the Cassandra database includes the default
1597 : * context and tables (domain, website, contents--although we only test
1598 : * for one single table.)
1599 : *
1600 : * This is called once each time the server is started. It does not matter
1601 : * too much as it is quite fast. Only one mandatory table gets checked. We
1602 : * may later provide a way for plugins to create different contexts but at
1603 : * this point we expect all of them to only make use of the Core provided
1604 : * context (a.k.a. "snap_websites").
1605 : *
1606 : * \todo
1607 : * If this function does not get called, the f_snapdbproxy_addr and
1608 : * f_snapdbproxy_port do not get defined. This is a problem that should
1609 : * be addressed at some point, even if the call is considered mandatory.
1610 : *
1611 : * \todo
1612 : * This function only checks for one table. Unfortunately, if all tables
1613 : * are not created before we accept connections, things will not work
1614 : * right. This will NOT be fixed here, however. Instead, we will change
1615 : * the snapdbproxy implementation to start in three steps: (1) connect
1616 : * to Cassandra, (2) make sure the snap_websites context exists, and
1617 : * (3) make sure all the known tables exist. Once all of these steps
1618 : * complete successfully, then snapdbproxy sends the CASSANDRAREADY.
1619 : *
1620 : * \param[in] mandatory_table A table that we expect to exist to go on.
1621 : * \param[out] timer_required Whether the caller should setup a timer to
1622 : * poll availability (true) or another CASSANDRAREADY message
1623 : * will be sent later (i.e. no context/tables when true.)
1624 : *
1625 : * \return true if Cassandra is considered valid (up/running/initialized).
1626 : */
1627 0 : bool server::check_cassandra(QString const & mandatory_table, bool & timer_required)
1628 : {
1629 0 : timer_required = false;
1630 :
1631 : try
1632 : {
1633 0 : snap_cassandra cassandra;
1634 :
1635 : // attempt a connection, this may throw if the connection fails
1636 : //
1637 0 : cassandra.connect();
1638 :
1639 : // make sure we have the "snap_websites" context
1640 : //
1641 0 : libdbproxy::context::pointer_t context( cassandra.get_snap_context() );
1642 0 : if( !context )
1643 : {
1644 : // CASSANDRAREADY will be sent to use again once the tables are
1645 : // created (which implies that the context exists)
1646 : //
1647 0 : SNAP_LOG_WARNING("snap_websites context does not exist! snapserver is going to sleep.");
1648 0 : return false;
1649 : }
1650 :
1651 : // make sure a certain table is ready so this daemon can run as
1652 : // expected; if not present, exit immediately
1653 : //
1654 : // XXX: The get_table() function throws if the table is not available
1655 : // and that triggers the cassandra_check_timer instead of just
1656 : // waiting for a new CASSANDRAREADY message. At some point we
1657 : // may want to look into a way to not throw if the table is
1658 : // not there, only throw if an actual error occurs.
1659 : //
1660 0 : if(!cassandra.get_table(mandatory_table))
1661 : {
1662 : // the table does not exist yet...
1663 : //
1664 : // tables are expected to be created from the *-tables.xml files
1665 : // (see snapdbproxy/tools/snapcreatetables for details.)
1666 : //
1667 : // CASSANDRAREADY will be sent to us again once the tables are
1668 : // created (which implies that the context exists)
1669 : //
1670 0 : SNAP_LOG_WARNING("\"")
1671 0 : (mandatory_table)
1672 0 : ("\" table does not exist! snapserver is going to sleep.");
1673 0 : return false;
1674 : }
1675 :
1676 : // save the snapdbproxy address and port so the children can quickly
1677 : // get that information
1678 : //
1679 0 : f_snapdbproxy_addr = cassandra.get_snapdbproxy_addr();
1680 0 : f_snapdbproxy_port = cassandra.get_snapdbproxy_port();
1681 :
1682 0 : return true;
1683 : }
1684 0 : catch(std::runtime_error const & e)
1685 : {
1686 0 : SNAP_LOG_WARNING("could not connect to the \"snapdbproxy\" daemon, context was not created, or table \"")
1687 0 : (mandatory_table)
1688 0 : ("\" is missing. Error: ")
1689 0 : (e.what());
1690 :
1691 : // In this case we are not going to ever receive another message
1692 : // to wake us up, so we want to use a timer and try again later
1693 : //
1694 0 : timer_required = true;
1695 0 : return false;
1696 : }
1697 : }
1698 :
1699 :
1700 : /** \brief Detach the server unless in foreground mode.
1701 : *
1702 : * This function detaches the server unless it is in foreground mode.
1703 : *
1704 : * \warning
1705 : * It is very important that you call the server_loop_ready() function
1706 : * after having called the detach() function. We expect that other
1707 : * function to be called just before you enter the server loop.
1708 : * In our case that generally means before calling the
1709 : * snap_communicator->run() function. That way, if anything in the
1710 : * initialization process fails and the loop is never enetered, we
1711 : * never signal systemd about being successful which is exactly what
1712 : * we want.
1713 : */
1714 0 : void server::detach()
1715 : {
1716 0 : if(f_foreground)
1717 : {
1718 0 : return;
1719 : }
1720 :
1721 : // create a new PID file, this gets a pipe ready for communication
1722 : // (i.e. the parent wants to wait for the child to be ready and
1723 : // have had time to create the PID file, something that can only
1724 : // happen after we called fork() unfortunately...)
1725 : //
1726 0 : f_pid_file.reset(new snap_pid(f_service_name));
1727 :
1728 : // detaching using fork()
1729 : //
1730 0 : pid_t const child_pid(fork());
1731 0 : if(child_pid == 0)
1732 : {
1733 : // this is the child, make sure we keep the log alive
1734 : //
1735 0 : logging::reconfigure();
1736 :
1737 : // at some point we want to save our PID in the PID file,
1738 : // this means we're ready (as far as systemd is concerned)
1739 : // but we want to do that just before entering the server
1740 : // loop because if anything fails before that happens we
1741 : // do not want to tell systemd that we succeessfully started
1742 : //
1743 0 : return;
1744 : }
1745 :
1746 0 : if(child_pid == -1)
1747 : {
1748 0 : logging::reconfigure();
1749 0 : SNAP_LOG_FATAL("the server could not fork() a child process to detach itself from your console.");
1750 0 : exit(1);
1751 : }
1752 :
1753 0 : int const code(f_pid_file->wait_signal() ? 0 : 1);
1754 :
1755 0 : exit(code);
1756 : }
1757 :
1758 :
1759 : /** \brief The server is ready to enter the server loop.
1760 : *
1761 : * If you use the detach() function, this function must be called just
1762 : * before you enter the server loop itself. In most cases, our server
1763 : * loop starts at the time we call the snap_communicator::run() function.
1764 : *
1765 : * This function finishes up the PID file initialization by creating the
1766 : * actual file and sending a signal to the parent process (through the
1767 : * pipe created for this purpose.)
1768 : *
1769 : * It is very important that this function gets called because otherwise
1770 : * the parent stays stuck waiting for its signal and instead of returning
1771 : * as expected, it will linger. This means systemd will not understand
1772 : * what the process status is.
1773 : */
1774 0 : void server::server_loop_ready()
1775 : {
1776 0 : if(f_pid_file != nullptr)
1777 : {
1778 0 : f_pid_file->create_pid_file();
1779 : }
1780 0 : }
1781 :
1782 :
1783 : /** \brief Send a PING message to the specified UDP server.
1784 : *
1785 : * This function sends a PING message (4 bytes) to the specified
1786 : * UDP server. This is used after you saved data in the Cassandra
1787 : * cluster to wake up a background process which can then "slowly"
1788 : * process the data further.
1789 : *
1790 : * Remember that UDP is not reliable so we do not in any way
1791 : * guarantee that this goes anywhere. The function returns no
1792 : * feedback at all. We do not wait for a reply since at the time
1793 : * we send the message the listening server may be busy. The
1794 : * idea of this ping is just to make sure that if the server is
1795 : * sleeping at that time, it wakes up sooner rather than later
1796 : * so it can immediately start processing the data we just added
1797 : * to Cassandra.
1798 : *
1799 : * The \p message is expected to be a NUL terminated string. The
1800 : * NUL is not sent across. At this point most of our servers
1801 : * accept a PING message to wake up and start working on new
1802 : * data.
1803 : *
1804 : * The \p upd_addr_port parameter is an IP address (IPv4 or IPv6)
1805 : * which must be followed by a colon and a port number.
1806 : *
1807 : * \warning
1808 : * The URI is expected to NOT include any port, path, query string
1809 : * options, anchor information. Only the protocol and full domain
1810 : * name ended by a slash.
1811 : *
1812 : * \param[in] service The name of the service to ping.
1813 : * \param[in] uri The website generating the ping.
1814 : */
1815 0 : void server::udp_ping_server( QString const & service, QString const & uri )
1816 : {
1817 0 : snap::snap_communicator_message ping;
1818 0 : ping.set_command("PING");
1819 0 : ping.set_service(service);
1820 0 : ping.add_parameter("uri", uri);
1821 :
1822 : // TBD: we may want to cache that information in case we call
1823 : // this function more than once
1824 : //
1825 0 : QString addr("127.0.0.1");
1826 0 : int port(4041);
1827 0 : QString const communicator_addr_port( f_parameters(QString("snapcommunicator"), "signal") );
1828 0 : tcp_client_server::get_addr_port(communicator_addr_port, addr, port, "udp");
1829 :
1830 0 : snap_communicator::snap_udp_server_message_connection::send_message(
1831 0 : addr.toUtf8().data()
1832 : , port
1833 : , ping
1834 0 : , f_parameters(QString("snapcommunicator"), "signal_secret").toUtf8().data());
1835 0 : }
1836 :
1837 :
1838 : /** \brief Send message to snapcomminucator about usage statistics.
1839 : *
1840 : * When a process ends, you may call this function in order to send
1841 : * its own statistics to the snapcommunicator. Any service can
1842 : * listen for the message to react to it in various ways.
1843 : *
1844 : * \param[in] process_name The name of the process dying.
1845 : */
1846 0 : void server::udp_rusage(QString const & process_name)
1847 : {
1848 : // retrieve the current usage information
1849 : //
1850 : struct rusage usage;
1851 0 : getrusage(RUSAGE_SELF, &usage);
1852 :
1853 : // log some basic information
1854 : //
1855 0 : SNAP_LOG_DEBUG("snap_child: used ")
1856 0 : (usage.ru_maxrss)
1857 0 : (" pages, ")
1858 0 : (usage.ru_utime.tv_sec) // TODO: add ".xxx"
1859 0 : (" seconds (user), and ")
1860 0 : (usage.ru_stime.tv_sec) // TODO: add ".xxx"
1861 0 : (" seconds (system).");
1862 :
1863 : // prepare a message to send to the snapwatchdog (via the snapcommunicator)
1864 : //
1865 : // TODO: make sure we get actual values, it looks like linux may not be
1866 : // defining much in the rusage structure... see:
1867 : // http://stackoverflow.com/questions/669438/how-to-get-memory-usage-at-run-time-in-c
1868 : //
1869 0 : snap::snap_communicator_message rusage_message;
1870 0 : rusage_message.set_command("RUSAGE");
1871 0 : rusage_message.set_server(get_server_name().c_str());
1872 0 : rusage_message.set_service("snapwatchdog");
1873 0 : rusage_message.add_parameter("cache", "ttl=10"); // cache for at most 10 seconds
1874 0 : rusage_message.add_parameter("process_name", process_name);
1875 0 : rusage_message.add_parameter("pid", getpid());
1876 0 : rusage_message.add_parameter("user_time", QString("%1.%2").arg(usage.ru_utime.tv_sec).arg(usage.ru_utime.tv_usec, 6, 10, QChar('0')));
1877 0 : rusage_message.add_parameter("system_time", QString("%1.%2").arg(usage.ru_stime.tv_sec).arg(usage.ru_stime.tv_usec, 6, 10, QChar('0')));
1878 0 : rusage_message.add_parameter("maxrss", QString("%1").arg(usage.ru_maxrss));
1879 0 : rusage_message.add_parameter("minor_page_fault", QString("%1").arg(usage.ru_minflt));
1880 0 : rusage_message.add_parameter("major_page_fault", QString("%1").arg(usage.ru_majflt));
1881 0 : rusage_message.add_parameter("in_block", QString("%1").arg(usage.ru_inblock));
1882 0 : rusage_message.add_parameter("out_block", QString("%1").arg(usage.ru_oublock));
1883 0 : rusage_message.add_parameter("volontary_context_switches", QString("%1").arg(usage.ru_nvcsw));
1884 0 : rusage_message.add_parameter("involontary_context_switches", QString("%1").arg(usage.ru_nivcsw));
1885 :
1886 : // TBD: we may want to cache that information in case we call
1887 : // this function more than once
1888 : //
1889 0 : QString addr("127.0.0.1");
1890 0 : int port(4041);
1891 0 : QString const communicator_addr_port( f_parameters(QString("snapcommunicator"), "signal") );
1892 0 : tcp_client_server::get_addr_port(communicator_addr_port, addr, port, "udp");
1893 :
1894 0 : snap_communicator::snap_udp_server_message_connection::send_message(
1895 0 : addr.toUtf8().data()
1896 : , port
1897 : , rusage_message
1898 0 : , f_parameters(QString("snapcommunicator"), "signal_secret").toUtf8().data());
1899 0 : }
1900 :
1901 :
1902 : /** \brief Block an IP address at the firewall level.
1903 : *
1904 : * This function sends a BLOCK message to the snapfirewall service in
1905 : * order to have the IP from the specified \p uri blocked for the
1906 : * specified \p period.
1907 : *
1908 : * The \p uri can include a scheme which represents the name of a protocol
1909 : * that needs to be blocked. At this time, we accept "http" and "smtp".
1910 : * Please use "http" for "https" since both ports will get blocked anyway.
1911 : *
1912 : * This function does not verify the name of the scheme. However, the
1913 : * snapfirewall will do so before using it.
1914 : *
1915 : * If the scheme is not defined, then the default, which is "http",
1916 : * is used.
1917 : *
1918 : * Supported schemes are defined under /etc/iplock/schemes and
1919 : * /etc/iplock/schemes/schemes.d for user defined schemes and
1920 : * modifications of system defined schemes.
1921 : *
1922 : * The \p period parameter is not required. If not specified, the default
1923 : * will apply. At this time, the snapfirewall tool uses "day" as its default.
1924 : * The supported periods are:
1925 : *
1926 : * \li "5min" -- this is mainly for test purposes, blocks the IP for 5 minutes.
1927 : * \li "hour" -- block the IP address for one hour.
1928 : * \li "day" -- block the IP address for 24h. (default)
1929 : * \li "week" -- block the IP address for 7 days.
1930 : * \li "month" -- block the IP address for 31 days.
1931 : * \li "year" -- block the IP address for 366 days.
1932 : * \li "forever" -- block the IP address for 5 years.
1933 : *
1934 : * \param[in] uri The IP address of to ban.
1935 : * \param[in] period The duration for which the ban applies.
1936 : * \param[in] reason A description for why the block is being requested.
1937 : */
1938 0 : void server::block_ip( QString const & uri, QString const & period, QString const & reason )
1939 : {
1940 : // create a server object (we are a static function!)
1941 : //
1942 0 : snap::server::pointer_t s( snap::server::instance() );
1943 :
1944 : // retrieve the IP and port to the snapcommunicator
1945 : //
1946 0 : QString addr("127.0.0.1");
1947 0 : int port(4041);
1948 0 : snap_config config("snapcommunicator");
1949 0 : tcp_client_server::get_addr_port(config["signal"], addr, port, "udp");
1950 :
1951 : // create a BLOCK message
1952 : //
1953 0 : snap::snap_communicator_message message;
1954 0 : message.set_command("BLOCK");
1955 0 : message.set_service("*"); // broadcast to all snapfirewall anywhere in our mesh
1956 :
1957 0 : message.add_parameter("uri", uri);
1958 :
1959 0 : if(!period.isEmpty())
1960 : {
1961 0 : message.add_parameter("period", period);
1962 : }
1963 : //else -- snapfirewall will use "day" by default
1964 :
1965 0 : if(!reason.isEmpty())
1966 : {
1967 0 : message.add_parameter("reason", reason);
1968 : }
1969 :
1970 : // send the message using a UDP signal
1971 : //
1972 0 : snap::snap_communicator::snap_udp_server_message_connection::send_message(
1973 0 : addr.toUtf8().data()
1974 : , port
1975 : , message
1976 : , config["signal_secret"]);
1977 0 : }
1978 :
1979 :
1980 0 : const snap_config& server::get_parameters() const
1981 : {
1982 0 : return f_parameters;
1983 : }
1984 :
1985 :
1986 : #ifdef SNAP_NO_FORK
1987 : /** \brief Don't fork the snap child if true.
1988 : *
1989 : * This is set via the command line. If set, the snap_child object will not fork.
1990 : *
1991 : * \note This is debug-only code, which should never be in production.
1992 : */
1993 : bool server::nofork() const
1994 : {
1995 : return f_nofork;
1996 : }
1997 : #endif
1998 :
1999 :
2000 :
2001 : /** \brief Check which child died.
2002 : *
2003 : * This function is used to find children that died and remove them
2004 : * from the list of zombies.
2005 : *
2006 : * \warning
2007 : * Although the signalfd() function returns a child PID, when you run
2008 : * parallel child processes and may get multiple SIGCHLD very quickly,
2009 : * you may miss a few with time. This means you could get zombies if
2010 : * you do not check all the children...
2011 : *
2012 : * \param[in] child_pid The process identification of the child that died.
2013 : */
2014 0 : void server::capture_zombies(pid_t child_pid)
2015 : {
2016 : // unfortunately, we cannot just do a waidpid() on child_pid specifically...
2017 : //
2018 : // TODO:
2019 : // we probably want to change the algorithm to be able to
2020 : // use waitpid(-1, ...) instead of looping through the list of
2021 : // all the running children each time (it is probably more effective
2022 : // than calling waitpid(child_pid, ...) for each existing child),
2023 : // however, at this point the check_status() function makes that
2024 : // call so we cannot have it here too...
2025 : //
2026 0 : NOTUSED(child_pid);
2027 :
2028 : // capture zombies first
2029 0 : snap_child_vector_t::size_type max_children(f_children_running.size());
2030 0 : for(snap_child_vector_t::size_type idx(0); idx < max_children; ++idx)
2031 : {
2032 : // note that some children could become ready "at the same time"
2033 : // (i.e. some SIGCHLD can be lost because a process is not expected
2034 : // to stack more than one signal number at a time...)
2035 : //
2036 0 : if(f_children_running[idx]->check_status() == snap_child::status_t::SNAP_CHILD_STATUS_READY)
2037 : {
2038 : // it is ready, so it can be reused now
2039 0 : f_children_waiting.push_back(f_children_running[idx]);
2040 0 : f_children_running.erase(f_children_running.begin() + idx);
2041 :
2042 : // removed one child so decrement index:
2043 0 : --idx;
2044 0 : --max_children;
2045 : }
2046 : }
2047 0 : }
2048 :
2049 :
2050 : /** \brief Capture children death.
2051 : *
2052 : * This class used used to create a connection on startup that allows
2053 : * us to know when a child dies. Whenever that happens, we get a call
2054 : * to the process_signal() callback.
2055 : */
2056 : class signal_child_death
2057 : : public snap_communicator::snap_signal
2058 : {
2059 : public:
2060 : typedef std::shared_ptr<signal_child_death> pointer_t;
2061 :
2062 : signal_child_death(server * s);
2063 : signal_child_death(signal_child_death const & rhs) = delete;
2064 0 : virtual ~signal_child_death() override {}
2065 :
2066 : signal_child_death & operator = (signal_child_death const & rhs) = delete;
2067 :
2068 : // snap_communicator::snap_signal implementation
2069 : virtual void process_signal() override;
2070 :
2071 : private:
2072 : // TBD: should this be a weak pointer?
2073 : server * f_server = nullptr;
2074 : };
2075 :
2076 :
2077 : /** \brief Initialize the child death signal.
2078 : *
2079 : * The function initializes the snap_signal to listen on the SIGCHLD
2080 : * Unix signal. It also saves the pointer \p s to the server so
2081 : * it can be used to call various functions in the server whenever
2082 : * the signal occurs.
2083 : *
2084 : * \param[in] s The server pointer.
2085 : */
2086 0 : signal_child_death::signal_child_death(server * s)
2087 : : snap_signal(SIGCHLD)
2088 0 : , f_server(s)
2089 : {
2090 0 : }
2091 :
2092 :
2093 : /** \brief Callback called each time the SIGCHLD signal occurs.
2094 : *
2095 : * This function gets called each time a child dies.
2096 : *
2097 : * The function checks all the children and removes zombies.
2098 : */
2099 0 : void signal_child_death::process_signal()
2100 : {
2101 : // check all our children and remove zombies
2102 : //
2103 0 : f_server->capture_zombies(get_child_pid());
2104 0 : }
2105 :
2106 :
2107 :
2108 :
2109 : /** \brief Timer to poll Cassandra's availability.
2110 : *
2111 : * This class is specifically used to pretend that we received a
2112 : * CASSANDRAREADY even when not sent to us. This is because when
2113 : * we check for the availability of Cassandra, it may not have the
2114 : * context and tables available yet. In that case, we would just
2115 : * fall asleep and do nothing more.
2116 : *
2117 : * This timer allows us to re-check for the Cassandra context and
2118 : * mandatory table as expected on a CASSANDRAREADY message.
2119 : */
2120 : class cassandra_check_timer
2121 : : public snap_communicator::snap_timer
2122 : {
2123 : public:
2124 : typedef std::shared_ptr<cassandra_check_timer> pointer_t;
2125 :
2126 : cassandra_check_timer(server * s);
2127 : cassandra_check_timer(cassandra_check_timer const & rhs) = delete;
2128 0 : virtual ~cassandra_check_timer() override {}
2129 :
2130 : cassandra_check_timer operator = (cassandra_check_timer const & rhs) = delete;
2131 :
2132 : // snap_communicator::snap_connection implementation
2133 : virtual void process_timeout() override;
2134 :
2135 : private:
2136 : // TBD: should this be a weak pointer?
2137 : server * f_server = nullptr;
2138 : };
2139 :
2140 :
2141 : /** \brief Initialize the timer as required.
2142 : *
2143 : * This disables the timer and sets up its ticks to send us a timeout
2144 : * event once per minute.
2145 : *
2146 : * So by default this timer does nothing.
2147 : *
2148 : * If the check_cassandra() function somehow fails in a way that means
2149 : * we would never get awaken again, then this timer gets turned on.
2150 : * It will be awaken be a timeout and send us a CASSANDRAREADY to
2151 : * simulate that something happened and we better recheck whether
2152 : * the Cassandra connection is now truly available.
2153 : *
2154 : * \param[in] s The server pointer.
2155 : */
2156 0 : cassandra_check_timer::cassandra_check_timer(server * s)
2157 : : snap_timer(60LL * 1000000LL)
2158 0 : , f_server(s)
2159 : {
2160 0 : set_name("cassandra check timer");
2161 0 : set_priority(1);
2162 0 : set_enable(false);
2163 0 : }
2164 :
2165 :
2166 : /** \brief The timer ticked.
2167 : *
2168 : * This function gets called each time the timer ticks. This is once
2169 : * per minute for this timer (see constructor).
2170 : *
2171 : * The timer is turned off (disabled) by default. It is used only if
2172 : * there is an error while trying to get the snap_websites context or a
2173 : * mandatory table.
2174 : *
2175 : * The function simulate a CASSANDRAREADY message as if the snapdbproxy
2176 : * service had sent it to us.
2177 : */
2178 0 : void cassandra_check_timer::process_timeout()
2179 : {
2180 : // disable ourselves, if the Cassandra cluster is still not ready,
2181 : // then we will automatically be re-enabled
2182 : //
2183 0 : set_enable(false);
2184 :
2185 : // simulate a CASSANDRAREADY message
2186 : //
2187 0 : snap_communicator_message cassandra_ready;
2188 0 : cassandra_ready.set_command("CASSANDRAREADY");
2189 0 : f_server->process_message(cassandra_ready);
2190 0 : }
2191 :
2192 :
2193 :
2194 :
2195 : /** \brief Listen and send messages with other services.
2196 : *
2197 : * This class is used to listen for incoming messages from
2198 : * snapcommunicator and also to send messages
2199 : *
2200 : * \note
2201 : * At this time we only send to snapwatchdog statistics at the time we die...
2202 : * but we may want to send more statistics about the children such as the
2203 : * count and other similar statistics. (i.e. we have to think about the time
2204 : * when we create listening children and in that case we do not want to
2205 : * count those children until they get a new connection; before that they
2206 : * do not count.)
2207 : *
2208 : * Also, we default to *not* using a thread to connect, because we are defaulting
2209 : * to the server. When used in a snap_child instance, you must override the default
2210 : * and set the flag to *true*. Otherwise bad things will happen.
2211 : */
2212 : class messenger
2213 : : public snap_communicator::snap_tcp_client_permanent_message_connection
2214 : {
2215 : public:
2216 : typedef std::shared_ptr<messenger> pointer_t;
2217 :
2218 : messenger(server * s, std::string const & addr, int port, bool const use_thread = false );
2219 : messenger(messenger const & rhs) = delete;
2220 0 : virtual ~messenger() override {}
2221 :
2222 : messenger & operator = (messenger const & rhs) = delete;
2223 :
2224 : // snap_communicator::snap_tcp_client_permanent_message_connection implementation
2225 : virtual void process_message(snap_communicator_message const & message) override;
2226 : virtual void process_connected() override;
2227 :
2228 : private:
2229 : server * f_server = nullptr;
2230 : };
2231 :
2232 :
2233 : /** \brief Initialize the messenger connection.
2234 : *
2235 : * This function initializes the messenger connection. It saves
2236 : * a pointer to the main Snap! server so it can react appropriately
2237 : * whenever a message is received.
2238 : *
2239 : * \param[in] s A pointer to the server so we can send messages there.
2240 : * \param[in] addr The address of the snapcommunicator server.
2241 : * \param[in] port The port of the snapcommunicator server.
2242 : * \param[in] use_thread If set to true, a thread is being used to connect to the server.
2243 : */
2244 0 : messenger::messenger(server * s, std::string const & addr, int port, bool const use_thread )
2245 : : snap_tcp_client_permanent_message_connection
2246 : ( addr
2247 : , port
2248 : , tcp_client_server::bio_client::mode_t::MODE_PLAIN
2249 : , snap_communicator::snap_tcp_client_permanent_message_connection::DEFAULT_PAUSE_BEFORE_RECONNECTING
2250 : , use_thread
2251 : )
2252 0 : , f_server(s)
2253 : {
2254 0 : }
2255 :
2256 :
2257 : /** \brief Process a message we just received.
2258 : *
2259 : * This function is called whenever the snapcommunicator received and
2260 : * decided to forward a message to us.
2261 : *
2262 : * \param[in] message The message we just received.
2263 : */
2264 0 : void messenger::process_message(snap_communicator_message const & message)
2265 : {
2266 0 : f_server->process_message(message);
2267 0 : }
2268 :
2269 :
2270 : /** \brief Process was just connected.
2271 : *
2272 : * This callback happens whenever a new connection is established.
2273 : * It sends a REGISTER command to the snapcommunicator. The READY
2274 : * reply will be received when process_message() gets called. At
2275 : * that point we are fully registered.
2276 : *
2277 : * This callback happens first so if we lose our connection to
2278 : * the snapcommunicator server, it will re-register the snapserver
2279 : * again as expected.
2280 : */
2281 0 : void messenger::process_connected()
2282 : {
2283 0 : snap_tcp_client_permanent_message_connection::process_connected();
2284 :
2285 0 : snap::snap_communicator_message register_snapserver;
2286 0 : register_snapserver.set_command("REGISTER");
2287 0 : register_snapserver.add_parameter("service", "snapserver");
2288 0 : register_snapserver.add_parameter("version", snap::snap_communicator::VERSION);
2289 0 : send_message(register_snapserver);
2290 0 : }
2291 :
2292 :
2293 : /** \brief Process a message received from Snap! Communicator.
2294 : *
2295 : * This function gets called whenever a message from snapcommunicator
2296 : * is received.
2297 : *
2298 : * The function reacts according to the message command:
2299 : *
2300 : * \li HELP -- reply with the COMMANDS message and the few commands we
2301 : * understand
2302 : * \li LOG -- reset the log
2303 : * \li READY -- ignored, this means Snap Communicator acknowledge that we
2304 : * registered with it
2305 : * \li STOP or QUITTING -- stop the server
2306 : * \li UNKNOWN -- ignored command, we log the fact that we sent an unknown
2307 : * message to someone
2308 : *
2309 : * If another command is received, the function replies with the UNKNOWN
2310 : * command to make sure the sender is aware that the command was ignored.
2311 : *
2312 : * \todo
2313 : * Convert to using a dispatcher.
2314 : *
2315 : * \param[in] message The message to process.
2316 : */
2317 0 : void server::process_message(snap_communicator_message const & message)
2318 : {
2319 0 : if(g_connection == nullptr
2320 0 : || g_connection->f_communicator == nullptr)
2321 : {
2322 0 : SNAP_LOG_WARNING("received message after the g_connection or g_connection->f_communicator variables were cleared.");
2323 0 : return;
2324 : }
2325 :
2326 0 : QString const command(message.get_command());
2327 :
2328 : // STATUS is sent too many times, so do not trace them all...
2329 0 : if(command != "STATUS")
2330 : {
2331 0 : SNAP_LOG_TRACE("received message [")(message.to_message())("] for server");
2332 : }
2333 :
2334 0 : if(command == "STOP")
2335 : {
2336 0 : stop(false);
2337 0 : return;
2338 : }
2339 0 : if(command == "QUITTING") // QUITTING happens when we send a message to snapcommunicator after it received a STOP
2340 : {
2341 0 : stop(true);
2342 0 : return;
2343 : }
2344 :
2345 0 : if(command == "LOG")
2346 : {
2347 0 : SNAP_LOG_INFO("Logging reconfiguration.");
2348 0 : logging::reconfigure();
2349 0 : return;
2350 : }
2351 :
2352 0 : if(command == "READY")
2353 : {
2354 : // TBD: should we start the listener here instead?
2355 : //
2356 : // the fact is... if we lose the connection to
2357 : // snapcommunicator we would start the listener
2358 : // at another time anyway
2359 : //
2360 :
2361 : // request snapdbproxy to send us a status signal about
2362 : // Cassandra, after that one call, we will receive the
2363 : // statuses just because we understand them.
2364 : //
2365 : {
2366 0 : snap::snap_communicator_message isdbready_message;
2367 0 : isdbready_message.set_command("CASSANDRASTATUS");
2368 0 : isdbready_message.set_service("snapdbproxy");
2369 0 : std::dynamic_pointer_cast<messenger>(g_connection->f_messenger)->send_message(isdbready_message);
2370 : }
2371 :
2372 : // request snapcommunicator to send us a STATUS message
2373 : // about the current status of the snaplock service
2374 : //
2375 : {
2376 : // TODO: we need to switch that to receiving the LOCKREADY
2377 : // and NOLOCK messages instead (i.e. see LOCKSTATUS)
2378 : //
2379 0 : snap::snap_communicator_message islockready_message;
2380 0 : islockready_message.set_command("SERVICESTATUS");
2381 0 : islockready_message.add_parameter("service", "snaplock");
2382 0 : std::dynamic_pointer_cast<messenger>(g_connection->f_messenger)->send_message(islockready_message);
2383 : }
2384 :
2385 : // request snapfirewall to send us a FIREWALLUP
2386 : // or a FIREWALLDOWN message
2387 : //
2388 0 : if(f_firewall_is_active)
2389 : {
2390 0 : snap::snap_communicator_message isfirewallready_message;
2391 0 : isfirewallready_message.set_command("FIREWALLSTATUS");
2392 0 : isfirewallready_message.set_service("snapfirewall");
2393 0 : std::dynamic_pointer_cast<messenger>(g_connection->f_messenger)->send_message(isfirewallready_message);
2394 : }
2395 : else
2396 : {
2397 : // this is not automatically true, but we will not have a
2398 : // way to know any better
2399 : //
2400 0 : f_firewall_up = true;
2401 : }
2402 :
2403 0 : return;
2404 : }
2405 :
2406 0 : if(command == "NOCASSANDRA")
2407 : {
2408 : // we lost Cassandra, disconnect from snapdbproxy until we
2409 : // get CASSANDRAREADY again
2410 : //
2411 0 : f_snapdbproxy_addr.clear();
2412 0 : f_snapdbproxy_port = 0;
2413 :
2414 0 : return;
2415 : }
2416 :
2417 0 : if(command == "CASSANDRAREADY")
2418 : {
2419 : // connect to Cassandra and verify that a "domains" table
2420 : // exists; the function returns false if not
2421 : //
2422 0 : bool timer_required(false);
2423 0 : if(!check_cassandra(get_name(name_t::SNAP_NAME_DOMAINS), timer_required))
2424 : {
2425 0 : if(timer_required && g_connection->f_cassandra_check_timer != nullptr)
2426 : {
2427 0 : g_connection->f_cassandra_check_timer->set_enable(true);
2428 : }
2429 : }
2430 0 : return;
2431 : }
2432 :
2433 0 : if(command == "FIREWALLUP")
2434 : {
2435 0 : f_firewall_up = true;
2436 0 : return;
2437 : }
2438 :
2439 0 : if(command == "FIREWALLDOWN")
2440 : {
2441 0 : f_firewall_up = false;
2442 0 : return;
2443 : }
2444 :
2445 0 : if(command == "STATUS")
2446 : {
2447 0 : if(message.get_parameter("service") == "snaplock")
2448 : {
2449 : // show the one STATUS that we manage here
2450 : //
2451 0 : SNAP_LOG_TRACE("received message [")(message.to_message())("]");
2452 :
2453 0 : f_snaplock = message.has_parameter("status")
2454 0 : && message.get_parameter("status") == "up";
2455 : }
2456 : // else -- ignore all others
2457 :
2458 0 : return;
2459 : }
2460 :
2461 0 : if(command == "HELP")
2462 : {
2463 0 : snap::snap_communicator_message reply;
2464 0 : reply.set_command("COMMANDS");
2465 :
2466 : // list of commands understood by server
2467 0 : reply.add_parameter("list", "CASSANDRAREADY,FIREWALLUP,HELP,LOG,NOCASSANDRA,QUITTING,READY,RELOADCONFIG,STATUS,STOP,UNKNOWN");
2468 :
2469 0 : std::dynamic_pointer_cast<messenger>(g_connection->f_messenger)->send_message(reply);
2470 0 : return;
2471 : }
2472 :
2473 0 : if(command == "RELOADCONFIG")
2474 : {
2475 0 : f_force_restart = true;
2476 0 : stop(false);
2477 0 : return;
2478 : }
2479 :
2480 0 : if(command == "UNKNOWN")
2481 : {
2482 0 : SNAP_LOG_ERROR("we sent unknown command \"")(message.get_parameter("command"))("\" and probably did not get the expected result.");
2483 0 : return;
2484 : }
2485 :
2486 : // unknown command is reported and process goes on
2487 : //
2488 0 : SNAP_LOG_ERROR("unsupported command \"")(command)("\" was received on the TCP connection.");
2489 : {
2490 0 : snap::snap_communicator_message reply;
2491 0 : reply.set_command("UNKNOWN");
2492 0 : reply.add_parameter("command", command);
2493 0 : std::dynamic_pointer_cast<messenger>(g_connection->f_messenger)->send_message(reply);
2494 : }
2495 0 : return;
2496 : }
2497 :
2498 :
2499 :
2500 : /** \brief Do the necessary to stop the Snap! server.
2501 : *
2502 : * This function closes the connections which as a result will stop
2503 : * the snapserver daemon.
2504 : *
2505 : * \param[in] quitting If true, the process received a QUITTING message.
2506 : */
2507 0 : void server::stop(bool quitting)
2508 : {
2509 0 : SNAP_LOG_INFO("Stopping server.");
2510 :
2511 0 : if(g_connection != nullptr
2512 0 : && g_connection->f_messenger != nullptr)
2513 : {
2514 0 : messenger::pointer_t msg(std::dynamic_pointer_cast<messenger>(g_connection->f_messenger));
2515 0 : if(quitting || (msg && !msg->is_connected()))
2516 : {
2517 : // turn off that connection now, we cannot UNREGISTER since
2518 : // we are not connected to snapcommunicator
2519 : //
2520 0 : g_connection->f_communicator->remove_connection(g_connection->f_messenger);
2521 0 : g_connection->f_messenger.reset();
2522 : }
2523 : else
2524 : {
2525 0 : if(msg)
2526 : {
2527 0 : msg->mark_done();
2528 : }
2529 :
2530 : // snapcommunicator is not quitting, so we also want to unregister
2531 : // to make sure everything works as expected
2532 : //
2533 0 : snap::snap_communicator_message cmd;
2534 0 : cmd.set_command("UNREGISTER");
2535 0 : cmd.add_parameter("service", "snapserver");
2536 0 : std::static_pointer_cast<messenger>(g_connection->f_messenger)->send_message(cmd);
2537 :
2538 : // f_messenger is expected to HUP after this
2539 : }
2540 : }
2541 :
2542 : {
2543 0 : g_connection->f_communicator->remove_connection(g_connection->f_listener);
2544 0 : g_connection->f_communicator->remove_connection(g_connection->f_child_death_listener);
2545 0 : g_connection->f_communicator->remove_connection(g_connection->f_interrupt);
2546 0 : g_connection->f_interrupt.reset();
2547 0 : g_connection->f_communicator->remove_connection(g_connection->f_cassandra_check_timer);
2548 0 : g_connection->f_cassandra_check_timer.reset();
2549 : }
2550 0 : }
2551 :
2552 :
2553 :
2554 :
2555 :
2556 :
2557 : /** \brief Handle the SIGINT that is expected to stop the server.
2558 : *
2559 : * This class is an implementation of the snap_signal that listens
2560 : * on the SIGINT.
2561 : */
2562 : class server_interrupt
2563 : : public snap::snap_communicator::snap_signal
2564 : {
2565 : public:
2566 : typedef std::shared_ptr<server_interrupt> pointer_t;
2567 :
2568 : server_interrupt(server * s);
2569 : server_interrupt(server_interrupt const & rhs) = delete;
2570 0 : virtual ~server_interrupt() override {}
2571 :
2572 : server_interrupt operator = (server_interrupt const & rhs) = delete;
2573 :
2574 : // snap::snap_communicator::snap_signal implementation
2575 : virtual void process_signal() override;
2576 :
2577 : private:
2578 : server * f_server = nullptr;
2579 : };
2580 :
2581 :
2582 : /** \brief The interrupt initialization.
2583 : *
2584 : * The interrupt uses the signalfd() function to obtain a way to listen on
2585 : * incoming Unix signals.
2586 : *
2587 : * Specifically, it listens on the SIGINT signal, which is the equivalent
2588 : * to the Ctrl-C.
2589 : *
2590 : * \param[in] s The server we are listening for.
2591 : */
2592 0 : server_interrupt::server_interrupt(server * s)
2593 : : snap_signal(SIGINT)
2594 0 : , f_server(s)
2595 : {
2596 0 : unblock_signal_on_destruction();
2597 0 : set_name("server interrupt");
2598 0 : }
2599 :
2600 :
2601 : /** \brief Call the stop function of the snaplock object.
2602 : *
2603 : * When this function is called, the signal was received and thus we are
2604 : * asked to quit as soon as possible.
2605 : */
2606 0 : void server_interrupt::process_signal()
2607 : {
2608 : // we simulate the STOP, so pass 'false' (i.e. not quitting)
2609 : //
2610 0 : f_server->stop(false);
2611 0 : }
2612 :
2613 :
2614 :
2615 :
2616 :
2617 : /** \brief Handle new connections from clients.
2618 : *
2619 : * This function is an implementation of the snap server so we can
2620 : * handle new connections from various clients.
2621 : */
2622 : class listener_impl
2623 : : public snap_communicator::snap_tcp_server_connection
2624 : {
2625 : public:
2626 : listener_impl(server * s, std::string const & addr, int port, std::string const & certificate, std::string const & private_key, int max_connections, bool reuse_addr);
2627 : listener_impl(listener_impl const & rhs) = delete;
2628 0 : virtual ~listener_impl() override {}
2629 :
2630 : listener_impl operator = (listener_impl const & rhs) = delete;
2631 :
2632 : // snap_communicator::snap_tcp_server_connection implementation
2633 : virtual void process_accept() override;
2634 :
2635 : private:
2636 : // this is owned by a server function so no need for a smart pointer
2637 : server * f_server = nullptr;
2638 : };
2639 :
2640 :
2641 :
2642 : /** \brief The listener initialization.
2643 : *
2644 : * The listener receives a pointer back to the snap::server object and
2645 : * information on how to generate the new network connection to listen
2646 : * on incoming connections from clients.
2647 : *
2648 : * The server listens to two types of messages:
2649 : *
2650 : * \li accept() -- a new connection is accepted from a client
2651 : * \li recv() -- a UDP message was received
2652 : *
2653 : * \param[in] s The server we are listening for.
2654 : * \param[in] addr The address to listen on. Most often it is 0.0.0.0.
2655 : * \param[in] port The port to listen on.
2656 : * \param[in] certificate The filename to a PEM file with a certificate.
2657 : * \param[in] private_key The filename to a PEM file with the private key.
2658 : * \param[in] max_connections The maximum number of connections to keep
2659 : * waiting; if more arrive, refuse them until we are done with
2660 : * some existing connections.
2661 : * \param[in] reuse_addr Whether to let the OS reuse that socket immediately.
2662 : */
2663 0 : listener_impl::listener_impl(server * s, std::string const & addr, int port, std::string const & certificate, std::string const & private_key, int max_connections, bool reuse_addr)
2664 : : snap_tcp_server_connection(
2665 : addr
2666 : , port
2667 : , certificate
2668 : , private_key
2669 0 : , (certificate.empty() && private_key.empty()) || addr == "127.0.0.1"
2670 : ? tcp_client_server::bio_server::mode_t::MODE_PLAIN
2671 : : tcp_client_server::bio_server::mode_t::MODE_SECURE
2672 : , max_connections
2673 : , reuse_addr)
2674 0 : , f_server(s)
2675 : {
2676 0 : non_blocking();
2677 0 : }
2678 :
2679 :
2680 : /** \brief This callback is called whenever a client tries to connect.
2681 : *
2682 : * This callback function is called whenever a new client tries to connect
2683 : * to the server.
2684 : *
2685 : * The function retrieves the new connection socket, makes the socket
2686 : * "keep alive" and then calls the process_connection() function of
2687 : * the server.
2688 : */
2689 0 : void listener_impl::process_accept()
2690 : {
2691 : // a new client just connected
2692 : //
2693 0 : tcp_client_server::bio_client::pointer_t const new_client(accept());
2694 0 : if(!new_client)
2695 : {
2696 : // TBD: should we call process_error() instead? problem is this
2697 : // listener would be removed from the list of connections...
2698 : //
2699 0 : int const e(errno);
2700 0 : SNAP_LOG_ERROR("accept() returned an error. (errno: ")(e)(" -- ")(strerror(e))("). No new connection will be created.");
2701 0 : return;
2702 : }
2703 :
2704 : // process the new connection, which means create a child process
2705 : // and run the necessary code to return an HTML page, a document,
2706 : // robots.txt, etc.
2707 : //
2708 0 : f_server->process_connection(new_client);
2709 : }
2710 :
2711 :
2712 : /** \brief Create the permanent messenger instance.
2713 : *
2714 : * This creates the messenger object, and hooks up the logger so we can send logs to
2715 : * snapcommunicator (and ultimately to snaplog).
2716 : *
2717 : * \param[in] use_thread If set to true, this will create a thread. Set to false if you are going to fork()
2718 : */
2719 0 : void server::create_messenger_instance( bool const use_thread )
2720 : {
2721 0 : if(g_connection == nullptr
2722 0 : || g_connection->f_communicator == nullptr)
2723 : {
2724 0 : SNAP_LOG_WARNING("attempt to create messenger after the g_connection or g_connection->f_communicator variables were cleared.");
2725 0 : return;
2726 : }
2727 :
2728 : // Remove the old connection (ignored if not connected)
2729 : //
2730 0 : g_connection->f_communicator->remove_connection( g_connection->f_messenger );
2731 :
2732 : // Get the communicator address/port
2733 : //
2734 0 : QString communicator_addr("127.0.0.1");
2735 0 : int communicator_port(4040);
2736 : tcp_client_server::get_addr_port
2737 0 : ( QString::fromUtf8(f_parameters("snapcommunicator", "local_listen").c_str())
2738 : , communicator_addr
2739 : , communicator_port
2740 : , "tcp"
2741 : );
2742 :
2743 : // Create a new messenger object
2744 : //
2745 0 : g_connection->f_messenger.reset(new messenger(this, communicator_addr.toUtf8().data(), communicator_port, use_thread));
2746 0 : g_connection->f_messenger->set_name("messenger");
2747 0 : g_connection->f_messenger->set_priority(50);
2748 :
2749 :
2750 : // Add it into the instance list.
2751 : //
2752 0 : g_connection->f_communicator->add_connection( g_connection->f_messenger );
2753 :
2754 : // Add this to the logging facility so we can broadcast logs to snaplog via snapcommunicator.
2755 : //
2756 0 : configure_messenger_logging(
2757 0 : std::static_pointer_cast<snap_communicator::snap_tcp_client_permanent_message_connection>( g_connection->f_messenger )
2758 : );
2759 : }
2760 :
2761 :
2762 : /** \brief Listen to incoming connections.
2763 : *
2764 : * This function initializes various connections which get added to
2765 : * the snap_communicator object. These connections are:
2766 : *
2767 : * \li A listener, which opens a port to listen to new incoming connections.
2768 : * \li A signal handler, also via a connection, which listens to the SIGCHLD
2769 : * Unix signal. This allows us to immediately manage zombie processes.
2770 : * \li A messenger, which is a permanent connection to the Snap Communicator
2771 : * server. Permanent because if the connection is lost, it will be
2772 : * reinstantiated as soon as possible.
2773 : *
2774 : * Our snap.cgi process is the one that connects to our listener, since at
2775 : * this time we do not directly listen to port 80 or 443.
2776 : *
2777 : * The messenger receives messages such as the STOP and LOG messages. The
2778 : * STOP message actually requests that this very function returns as soon
2779 : * as the server is done with anything it is currently doing.
2780 : *
2781 : * If the function finds an error in one of the parameters used from the
2782 : * configuration file, then it logs an error and calls exit(1).
2783 : *
2784 : * Other errors may occur in which case it is likely that the process
2785 : * will throw an error.
2786 : */
2787 0 : void server::listen()
2788 : {
2789 0 : SNAP_LOG_INFO("--------------------------------- snapserver started on ")(get_server_name());
2790 :
2791 : // offer the user to setup the maximum number of pending connections
2792 0 : long max_pending_connections(-1);
2793 : bool ok;
2794 0 : QString max_connections(f_parameters["max_pending_connections"]);
2795 0 : if(!max_connections.isEmpty())
2796 : {
2797 0 : max_pending_connections = max_connections.toLong(&ok);
2798 0 : if(!ok)
2799 : {
2800 0 : SNAP_LOG_FATAL("invalid max_pending_connections, a valid number was expected instead of \"")(max_connections)("\".");
2801 0 : exit(1);
2802 : }
2803 0 : if(max_pending_connections < 1)
2804 : {
2805 0 : SNAP_LOG_FATAL("max_pending_connections must be positive, \"")(max_connections)("\" is not valid.");
2806 0 : exit(1);
2807 : }
2808 : }
2809 :
2810 : // get the address/port info
2811 0 : QString addr("127.0.0.1");
2812 0 : int port(4004);
2813 0 : tcp_client_server::get_addr_port(f_parameters["listen"], addr, port, "tcp");
2814 :
2815 : // convert the address information
2816 0 : QHostAddress const a(addr);
2817 0 : if(a.isNull())
2818 : {
2819 0 : SNAP_LOG_FATAL("invalid address specification in \"")(addr)(":")(port)("\".");
2820 0 : exit(1);
2821 : }
2822 :
2823 : // get timeout time for wait when children exist
2824 0 : long timeout_wait_children(5000);
2825 0 : QString timeout_wait_children_param(f_parameters["timeout_wait_children"]);
2826 0 : if(!timeout_wait_children_param.isEmpty())
2827 : {
2828 0 : timeout_wait_children = timeout_wait_children_param.toLong(&ok);
2829 0 : if(!ok)
2830 : {
2831 0 : SNAP_LOG_FATAL("invalid timeout_wait_children, a valid number was expected instead of \"")(timeout_wait_children_param)("\".");
2832 0 : exit(1);
2833 : }
2834 0 : if(timeout_wait_children < 100)
2835 : {
2836 0 : SNAP_LOG_FATAL("timeout_wait_children must be at least 100, \"")(timeout_wait_children_param)("\" is not acceptable.");
2837 0 : exit(1);
2838 : }
2839 : }
2840 :
2841 : // get the SSL certificate and private key paths
2842 : //
2843 0 : std::string const certificate(f_parameters["ssl_certificate"]);
2844 0 : std::string const private_key(f_parameters["ssl_private_key"]);
2845 :
2846 : // get the snapcommunicator IP and port
2847 0 : QString communicator_addr("127.0.0.1");
2848 0 : int communicator_port(4040);
2849 0 : tcp_client_server::get_addr_port(QString::fromUtf8(f_parameters("snapcommunicator", "local_listen").c_str())
2850 : , communicator_addr
2851 : , communicator_port
2852 : , "tcp");
2853 :
2854 : // TBD: Would we need a lock sooner? if so, we are in trouble...
2855 : // Initialize the snap communicator information in snap_lock
2856 : // so locks work as expected.
2857 : //
2858 : // We keep the default timeout but various processes may change that to
2859 : // a different value as required.
2860 : //
2861 0 : snap::snap_lock::initialize_snapcommunicator(communicator_addr.toUtf8().data(), communicator_port);
2862 :
2863 : // create a communicator
2864 : //
2865 : // only we use a bare pointer because otherwise the child processes
2866 : // attempt to destroy these objects and that does not work right
2867 : //
2868 0 : g_connection = new connection_t;
2869 0 : g_connection->f_communicator = snap_communicator::instance();
2870 :
2871 : // capture Ctrl-C (SIGINT)
2872 : //
2873 0 : g_connection->f_interrupt.reset(new server_interrupt(this));
2874 0 : g_connection->f_communicator->add_connection(g_connection->f_interrupt);
2875 :
2876 : // create a listener, for new arriving client connections
2877 : //
2878 : // auto-close is set to false because the accept() is not directly used
2879 : // on the tcp_server object
2880 : //
2881 0 : g_connection->f_listener.reset(new listener_impl(this, addr.toUtf8().data(), port, certificate, private_key, max_pending_connections, true));
2882 0 : g_connection->f_listener->set_name("server listener");
2883 0 : g_connection->f_listener->set_priority(30);
2884 0 : g_connection->f_communicator->add_connection(g_connection->f_listener);
2885 :
2886 0 : g_connection->f_child_death_listener.reset(new signal_child_death(this));
2887 0 : g_connection->f_child_death_listener->set_name("child death listener");
2888 0 : g_connection->f_child_death_listener->set_priority(75);
2889 0 : g_connection->f_communicator->add_connection(g_connection->f_child_death_listener);
2890 :
2891 0 : g_connection->f_cassandra_check_timer.reset(new cassandra_check_timer(this));
2892 0 : g_connection->f_communicator->add_connection(g_connection->f_cassandra_check_timer);
2893 :
2894 0 : create_messenger_instance();
2895 :
2896 : // the server was successfully started
2897 0 : SNAP_LOG_INFO("Snap v" SNAPWEBSITES_VERSION_STRING " on \"")(get_server_name())("\" started.");
2898 :
2899 0 : server_loop_ready();
2900 :
2901 : // run until we get killed
2902 0 : g_connection->f_communicator->run();
2903 :
2904 : // if we are returning that is because the signals were removed from
2905 : // the communicator so we can now destroy the communicator
2906 0 : g_connection->f_communicator.reset();
2907 :
2908 0 : if(f_force_restart)
2909 : {
2910 0 : exit(1);
2911 : }
2912 0 : }
2913 :
2914 :
2915 : /** \brief Process an incoming connection.
2916 : *
2917 : * This function processes an incoming connection from a client.
2918 : * This connection is from the snap.cgi to the snapserver.
2919 : *
2920 : * \param[in] client The client which represents the new connection.
2921 : */
2922 0 : void server::process_connection(tcp_client_server::bio_client::pointer_t client)
2923 : {
2924 : // we are handling one more connection, whether it works or
2925 : // not we increase our internal counter
2926 0 : ++f_connections_count;
2927 :
2928 : // make sure the database connection is ready, if not, we just
2929 : // reply with an instant error
2930 0 : if(f_snapdbproxy_addr.isEmpty())
2931 : {
2932 0 : if(!f_snaplock)
2933 : {
2934 0 : SNAP_LOG_DEBUG("snapserver contacted before cassandra and snaplock are ready.");
2935 : }
2936 : else
2937 : {
2938 0 : SNAP_LOG_DEBUG("snapserver contacted before cassandra is ready.");
2939 : }
2940 : std::string const err("Status: 503 Service Unavailable\n"
2941 : "Expires: Sun, 19 Nov 1978 05:00:00 GMT\n"
2942 : "Content-type: text/html\n"
2943 : "Connection: close\n"
2944 : "\n"
2945 : "<h1>503 Service Unavailable</h1>\n"
2946 0 : "<p>Snap cannot find <strong>Cassandra</strong> at the moment.</p>\n");
2947 0 : NOTUSED(client->write(err.c_str(), err.size()));
2948 : }
2949 0 : else if(!f_snaplock)
2950 : {
2951 0 : SNAP_LOG_DEBUG("snapserver contacted before snaplock is ready.");
2952 : std::string const err("Status: 503 Service Unavailable\n"
2953 : "Expires: Sun, 19 Nov 1978 05:00:00 GMT\n"
2954 : "Content-type: text/html\n"
2955 : "Connection: close\n"
2956 : "\n"
2957 : "<h1>503 Service Unavailable</h1>\n"
2958 0 : "<p>Cannot find <strong>Snap! Lock</strong> at the moment.</p>\n");
2959 0 : NOTUSED(client->write(err.c_str(), err.size()));
2960 : }
2961 0 : else if(!f_firewall_up)
2962 : {
2963 0 : SNAP_LOG_DEBUG("snapserver contacted before snapfirewall is ready.");
2964 : std::string const err("Status: 503 Service Unavailable\n"
2965 : "Expires: Sun, 19 Nov 1978 05:00:00 GMT\n"
2966 : "Content-type: text/html\n"
2967 : "Connection: close\n"
2968 : "\n"
2969 : "<h1>503 Service Unavailable</h1>\n"
2970 0 : "<p>Cannot find <strong>Snap! Firewall</strong> at the moment.</p>\n");
2971 0 : NOTUSED(client->write(err.c_str(), err.size()));
2972 : }
2973 : else
2974 : {
2975 0 : snap_child * child(nullptr);
2976 :
2977 0 : if(f_children_waiting.empty())
2978 : {
2979 0 : child = new snap_child(g_instance);
2980 : }
2981 : else
2982 : {
2983 0 : child = f_children_waiting.back();
2984 0 : f_children_waiting.pop_back();
2985 : }
2986 :
2987 0 : if(child->process(client))
2988 : {
2989 : // this child is now busy
2990 : //
2991 0 : f_children_running.push_back(child);
2992 : }
2993 : else
2994 : {
2995 : // it failed, we can keep that child as a waiting child
2996 : //
2997 0 : f_children_waiting.push_back(child);
2998 :
2999 : // and tell the user about a problem without telling much...
3000 : // (see the logs for more info.)
3001 : // TBD Translation?
3002 : //
3003 : std::string const err("Status: 503 Service Unavailable\n"
3004 : "Expires: Sun, 19 Nov 1978 05:00:00 GMT\n"
3005 : "Content-type: text/html\n"
3006 : "Connection: close\n"
3007 : "\n"
3008 : "<h1>503 Service Unavailable</h1>\n"
3009 0 : "<p>Server cannot start child process.</p>\n");
3010 0 : NOTUSED(client->write(err.c_str(), err.size()));
3011 : }
3012 : }
3013 0 : }
3014 :
3015 :
3016 : /** \brief Handle caught signals
3017 : *
3018 : * Catch the signal, then log the signal, then terminate with 1 status.
3019 : */
3020 0 : void server::sighandler( int sig )
3021 : {
3022 0 : std::string signame;
3023 0 : bool output_stack_trace(true);
3024 0 : switch( sig )
3025 : {
3026 0 : case SIGSEGV : signame = "SIGSEGV"; break;
3027 0 : case SIGBUS : signame = "SIGBUS"; break;
3028 0 : case SIGFPE : signame = "SIGFPE"; break;
3029 0 : case SIGILL : signame = "SIGILL"; break;
3030 0 : case SIGTERM : signame = "SIGTERM"; output_stack_trace = false; break;
3031 0 : case SIGINT : signame = "SIGINT"; output_stack_trace = false; break;
3032 0 : case SIGQUIT : signame = "SIGQUIT"; output_stack_trace = false; break;
3033 0 : case SIGALRM : signame = "SIGALRM"; break;
3034 0 : case SIGABRT : signame = "SIGABRT"; break;
3035 0 : default : signame = "UNKNOWN"; break;
3036 : }
3037 :
3038 0 : SNAP_LOG_FATAL("POSIX signal caught: ")(signame);
3039 :
3040 0 : if( output_stack_trace )
3041 : {
3042 0 : snap_exception_base::output_stack_trace();
3043 : }
3044 :
3045 : // is server available?
3046 : //
3047 0 : if(g_instance)
3048 : {
3049 0 : g_instance->exit(1);
3050 : }
3051 :
3052 : // server not available, exit directly
3053 : //
3054 0 : ::exit(1);
3055 : }
3056 :
3057 :
3058 : /** \brief Capture POSIX signals, log that they happened, and continue.
3059 : *
3060 : * This function is a callback we use to capture certain signals that
3061 : * we want to know of but do not want to kill the process.
3062 : *
3063 : * The function logs the fact that the signal occurred and then returns.
3064 : * This means the software continues to run. The function that generated
3065 : * the signal should fail, in many cases, meaning that it returns -1 or
3066 : * some similar error code. errno should then be set to EINTR. It is
3067 : * the responsibility of the caller to properly handle such error codes.
3068 : *
3069 : * \note
3070 : * Signals such as SIGSEGV and SIGILL should never use this function
3071 : * since those signals are considered terminal.
3072 : *
3073 : * \param[in] sig The signal that just generated an interrupt.
3074 : */
3075 0 : void server::sigloghandler( int sig )
3076 : {
3077 0 : std::string signame;
3078 :
3079 0 : switch( sig )
3080 : {
3081 0 : case SIGPIPE : signame = "SIGPIPE"; break;
3082 0 : default : signame = "UNKNOWN"; break;
3083 : }
3084 :
3085 : // in most cases we do not want to waste time with the stack trace here
3086 : // but if you need it, just uncomment the next line, just try NOT commit
3087 : // it uncommented because that could then end up in a release version...
3088 : //
3089 : //snap_exception_base::output_stack_trace();
3090 :
3091 0 : SNAP_LOG_WARNING("POSIX signal caught: ")(signame);
3092 :
3093 : // in this case we return because we want the process to continue
3094 : //
3095 0 : return;
3096 : }
3097 :
3098 :
3099 : /** \brief Run the backend process.
3100 : *
3101 : * This function creates a child and runs its backend function.
3102 : *
3103 : * The function may first initialize some more things in the server.
3104 : *
3105 : * When the backend process ends, the function returns. Assuming everything
3106 : * works as expected, the function is exepcted to return cleanly.
3107 : */
3108 0 : void server::backend()
3109 : {
3110 0 : snap_backend the_backend(g_instance);
3111 0 : the_backend.run_backend();
3112 0 : }
3113 :
3114 :
3115 : /** \brief Return the number of connections received by the server.
3116 : *
3117 : * This function returns the connections counter. Note that this
3118 : * counter is just an in memory counter so once the server restarts
3119 : * it is reset to zero.
3120 : */
3121 0 : unsigned long server::connections_count()
3122 : {
3123 0 : return f_connections_count;
3124 : }
3125 :
3126 :
3127 : /** \brief Servername, taken from argv[0].
3128 : *
3129 : * This method returns the server name, taken from the first argument on the command line.
3130 : */
3131 0 : std::string server::servername() const
3132 : {
3133 0 : return f_servername;
3134 : }
3135 :
3136 :
3137 0 : void server::configure_messenger_logging( snap_communicator::snap_tcp_client_permanent_message_connection::pointer_t ptr )
3138 : {
3139 0 : if( f_opt->is_defined("no-messenger-logging") )
3140 : {
3141 0 : return;
3142 : }
3143 :
3144 0 : logging::set_log_messenger( ptr );
3145 : }
3146 :
3147 :
3148 : /** \fn void server::init()
3149 : * \brief Initialize the Snap Websites server.
3150 : *
3151 : * This function readies the Init signal.
3152 : *
3153 : * At this time, it does nothing.
3154 : */
3155 :
3156 :
3157 : /** \fn void server::update(int64_t last_updated)
3158 : * \brief Update the Snap Websites server.
3159 : *
3160 : * This signal ensures that the data managed by this plugin is up to date.
3161 : *
3162 : * \param[in] last_updated The date and time when the website was last updated.
3163 : */
3164 :
3165 :
3166 : /** \fn void server::process_cookies()
3167 : * \brief Process a cookies on an HTTP request.
3168 : *
3169 : * This signal is used to
3170 : *
3171 : * At this time, it does nothing.
3172 : */
3173 :
3174 :
3175 : /** \fn void server::attach_to_session()
3176 : * \brief Process the attach to session event.
3177 : *
3178 : * This signal gives plugins a chance to attach data to the session right
3179 : * before the process ends.
3180 : */
3181 :
3182 :
3183 : /** \fn void server::detach_from_session()
3184 : * \brief Process the detach from session event.
3185 : *
3186 : * This signal gives plugins a chance to detach data that they attached to
3187 : * the sessions earlier. This signal happens early on after the process
3188 : * initialized the plugins.
3189 : */
3190 :
3191 :
3192 : /** \fn void server::define_locales(http_strings::WeightedHttpString & locales)
3193 : * \brief Give plugins a chance to define the acceptable page locales.
3194 : *
3195 : * This signal is used to give a chance to plugins to define the user's
3196 : * locale. Note that the user locale is in second position. If the
3197 : * information was defined in the URI (query string, path, domain, or
3198 : * sub-domain...) then the URI locale gets used if it exists.
3199 : *
3200 : * In most cases, on the `users` plugin should deal with this signal.
3201 : * However, other plugins may also accept this signal and add to the
3202 : * list of locales.
3203 : *
3204 : * This signals passes a \p locales parameter which can be used to
3205 : * add more locales to the acceptable locales. In most cases, all
3206 : * you want to do is call the parse() function as in:
3207 : *
3208 : * \code
3209 : * // add Spanish as a choice, albeit very low level
3210 : * locales.parse("es;q=0.1");
3211 : * \endcode
3212 : *
3213 : * Other example of language strings:
3214 : *
3215 : * \code
3216 : * # as is (spaces not required)
3217 : * "de, fr"
3218 : *
3219 : * # the same with weights (again, spaces not required)
3220 : * "de; q=0.9, fr; q=0.3"
3221 : * \endcode
3222 : *
3223 : * If you already have the language, country, and level separated, you
3224 : * may instead create a part_t and push it on the vector. At this time
3225 : * you have to retrieve a reference to the vector to do the push.
3226 : *
3227 : * \code
3228 : * http_strings::WeightedHttpString::part_t p;
3229 : * p.set_language("es");
3230 : * p.set_country("PE"); // optional
3231 : * p.set_level(0.1);
3232 : * locales.get_parts().push_back(p);
3233 : * \endcode
3234 : *
3235 : * You do not have to worry about the sort order, the caller will take
3236 : * care of it.
3237 : *
3238 : * \warning
3239 : * At this time the sort is executed after we are done with this function
3240 : * and that can mean that the order in which the final set of locales is
3241 : * given has nothing to do with the order in which the various signal
3242 : * implementations get called.
3243 : *
3244 : * \param[in,out] locales The variable receiving the locales.
3245 : */
3246 :
3247 :
3248 : /** \fn void server::process_post(QString const & url)
3249 : * \brief Process a POST request at the specified URL.
3250 : *
3251 : * This signal is sent when the server is called with a POST instead of a GET.
3252 : *
3253 : * \param[in] url The URL to process a POST from.
3254 : */
3255 :
3256 :
3257 : /** \fn void server::execute(QString const & url)
3258 : * \brief Execute the URL.
3259 : *
3260 : * This signal is called once the plugins were fully initialized. At this point
3261 : * only one plugin is expected to implement that signal: path.
3262 : *
3263 : * \param[in] url The URL to execute.
3264 : */
3265 :
3266 :
3267 : /** \fn void server::register_backend_cron(backend_action_set & actions)
3268 : * \brief Execute the specified backend action every 5 minutes.
3269 : *
3270 : * This signal is called when the backend server is asked to run a backend
3271 : * service as a CRON server. By default the service runs once every five
3272 : * minutes. It can also be awaken with a PING message and ended with a STOP
3273 : * message.
3274 : *
3275 : * Plugins that handle backend work that happens on a regular schedule and
3276 : * has to be available quickly through a PING are registered using this
3277 : * signal.
3278 : *
3279 : * If you are writing a one time signal process, use the
3280 : * register_backend_action() instead. And if you are writing a backend
3281 : * process that does not need to be quickly awaken via a PING, use the
3282 : * regular process_backend() signal.
3283 : *
3284 : * The actions are run through the on_backend_action() virtual function
3285 : * of the backend_action structure.
3286 : *
3287 : * \param[in,out] actions A map where plugins can register the actions they support.
3288 : */
3289 :
3290 :
3291 : /** \fn void server::register_backend_action(backend_action_set & actions)
3292 : * \brief Execute the specified backend action.
3293 : *
3294 : * This signal is called when the server is run as a backend service.
3295 : * Plugins that handle backend work can register when this signal is
3296 : * triggered.
3297 : *
3298 : * If you want to register a backend that executes every five minutes
3299 : * and can quickly be awaken using a PING event, then use the
3300 : * register_backend_cron() function instead. For actions that are to
3301 : * run over and over again instead of once in a while on an explicit
3302 : * call, implement the backend_process() instead.
3303 : *
3304 : * The actions are run through the on_backend_action() virtual function
3305 : * of the backend_action structure.
3306 : *
3307 : * \param[in,out] actions A map where plugins can register the actions they support.
3308 : */
3309 :
3310 :
3311 : /** \fn void server::backend_process()
3312 : * \brief Execute the backend processes.
3313 : *
3314 : * This signal runs backend processes that do not need to be run immediately
3315 : * when something changes in the database. For example, the RSS feeds are
3316 : * updated when this signal calls the feed on_backend_process()
3317 : * implementation.
3318 : *
3319 : * The signal is sent to all plugins that registered with the standard
3320 : * SNAP_LISTEN() macro. The function is expected to do some work and then
3321 : * return. The function should not take too long or it has to verify
3322 : * whether the STOP event was sent to the backend.
3323 : *
3324 : * This implementation is different from the backend actions which are
3325 : * either permanent (register_backend_cron) or a one time call
3326 : * (register_backend_action). The CRON actions stay and run forever and
3327 : * can be awaken by a PING event.
3328 : */
3329 :
3330 :
3331 : /** \fn void server::save_content()
3332 : * \brief Request new content to be saved.
3333 : *
3334 : * This signal is sent after the update signal returns. This gives a chance
3335 : * to the content plugin to save the data. It is somewhat specialized at this
3336 : * point, unfortunately, but it has the advantage of working properly.
3337 : */
3338 :
3339 :
3340 : /** \fn void server::xss_filter(QDomNode & node, QString const & acceptable_tags, QString const & acceptable_attributes)
3341 : * \brief Implementation of the XSS filter signal.
3342 : *
3343 : * This signal is used to clean any possible XSS potential problems in the specified
3344 : * \p node.
3345 : *
3346 : * \param[in,out] node The HTML node to check with XSS filters.
3347 : * \param[in] acceptable_tags The tags kept in the specified HTML.
3348 : * (i.e. "p a ul li")
3349 : * \param[in] acceptable_attributes The list of (not) acceptable attributes
3350 : * (i.e. "!styles")
3351 : *
3352 : * \return true if the signal has to be sent to other plugins.
3353 : */
3354 :
3355 :
3356 : /** \fn permission_error_callback::on_error(snap_child::http_code_t const err_code, QString const& err_name, QString const& err_description, QString const& err_details, bool const err_by_mime_type)
3357 : * \brief Generate an error.
3358 : *
3359 : * This function is called if an error is generated. If so then the function
3360 : * should mark the permission as not available for that user.
3361 : *
3362 : * This function accepts the same parameters as the snap_child::die()
3363 : * function.
3364 : *
3365 : * This implementation of the function does not returned. However, it cannot
3366 : * expect that all implementations would not return (to the contrary!)
3367 : *
3368 : * \param[in] err_code The error code such as 501 or 503.
3369 : * \param[in] err_name The name of the error such as "Service Not Available".
3370 : * \param[in] err_description HTML message about the problem.
3371 : * \param[in] err_details Server side text message with details that are logged only.
3372 : * \param[in] err_by_mime_type If returning an error, do not return HTML when this element MIME type is something else, instead send a file of that type, but still with the HTTP error code supplied.
3373 : */
3374 :
3375 :
3376 : /** \fn permission_error_callback::on_redirect(QString const& err_name, QString const& err_description, QString const& err_details, bool err_security, QString const& path, snap_child::http_code_t const http_code)
3377 : * \brief Generate a message and redirect the user.
3378 : *
3379 : * This function is called if an error is generated, but an error that can
3380 : * be "fixed" (in most cases by having the user log in or enter his
3381 : * credentials for a higher level of security on the website.)
3382 : *
3383 : * This function accepts the same parameters as the message::set_error()
3384 : * function followed by the same parameters as the snap_child::redirect()
3385 : * function.
3386 : *
3387 : * This implementation of the function does not returned. However, it cannot
3388 : * expect that all implementations would not return (to the contrary!)
3389 : *
3390 : * \param[in] err_name The name of the error such as "Value Too Small".
3391 : * \param[in] err_description HTML message about the problem.
3392 : * \param[in] err_details Server side text message with details that are logged only.
3393 : * \param[in] err_security Whether this message is considered a security related message.
3394 : * \param[in] path The path where the user is being redirected.
3395 : * \param[in] http_code The code to use while redirecting the user.
3396 : */
3397 :
3398 :
3399 : /** \fn void server::improve_signature(QString const& path, QString& signature)
3400 : * \brief Improve the die() signature to add at the bottom of pages.
3401 : *
3402 : * This function calls all the plugins that define the signature signal so
3403 : * they can append their own link or other information to the signature.
3404 : * The signature is a simple mechanism to get several plugins to link to
3405 : * a page where the user can go to continue his browsing on that website.
3406 : * In most cases it is never called because the site will have a
3407 : * page_not_found() call which is answered properly and thus a regular
3408 : * error page is shown.
3409 : *
3410 : * The signature parameter is passed to the plugins as an in/out parameter.
3411 : * The plugins are free to do whatever they want, including completely
3412 : * overwrite the content although keep in mind that you cannot ensure
3413 : * that a plugin is last in the last. In most cases you should limit
3414 : * yourself to doing something like this:
3415 : *
3416 : * \code
3417 : * signature += " <a href=\"/search\">Search This Website</a>";
3418 : * \endcode
3419 : *
3420 : * This very function does nothing, just returthisns true.
3421 : *
3422 : * \param[in] path The path that generated the error
3423 : * \param[in,out] signature The signature as inline HTML code (i.e. no blocks!)
3424 : *
3425 : * \return true if the signal has to be sent to other plugins.
3426 : */
3427 :
3428 :
3429 : /** \brief Load a file.
3430 : *
3431 : * This function is used to load a file. As additional plugins are added
3432 : * additional protocols can be supported.
3433 : *
3434 : * The file information defaults are kept as is as much as possible. If
3435 : * a plugin returns a file, though, it is advised that any information
3436 : * available to the plugin be set in the file object.
3437 : *
3438 : * The base load_file() function (i.e. this very function) supports the
3439 : * file system protocol (file:) and the Qt resources protocol (qrc:).
3440 : * Including the "file:" protocol is not required. Also, the Qt resources
3441 : * can be indicated simply by adding a colon at the beginning of the
3442 : * filename (":/such/as/this/name").
3443 : *
3444 : * \param[in,out] file The file name and content.
3445 : * \param[in,out] found Whether the file was found.
3446 : *
3447 : * \return true if the signal is to be propagated to all the plugins.
3448 : */
3449 0 : bool server::load_file_impl(snap_child::post_file_t & file, bool & found)
3450 : {
3451 0 : QString filename(file.get_filename());
3452 :
3453 0 : found = false;
3454 :
3455 0 : int const colon_pos(filename.indexOf(':'));
3456 0 : int const slash_pos(filename.indexOf('/'));
3457 0 : if(colon_pos <= 0 // no protocol
3458 0 : || colon_pos > slash_pos // no protocol
3459 0 : || filename.startsWith("file:") // file protocol
3460 0 : || filename.startsWith("qrc:")) // Qt resource protocol
3461 : {
3462 0 : if(filename.startsWith("file:"))
3463 : {
3464 : // remove the protocol
3465 0 : filename = filename.mid(5);
3466 : }
3467 0 : else if(filename.startsWith("qrc:"))
3468 : {
3469 : // remove the protocol, but keep the colon
3470 0 : filename = filename.mid(3);
3471 : }
3472 0 : QFile f(filename);
3473 0 : if(!f.open(QIODevice::ReadOnly))
3474 : {
3475 : // file not found...
3476 0 : SNAP_LOG_ERROR("error trying to read file \"")(filename)("\", system error: ")(f.errorString());
3477 0 : return false;
3478 : }
3479 0 : file.set_filename(filename);
3480 0 : file.set_data(f.readAll());
3481 0 : found = true;
3482 : // return false since we already "found" the file
3483 0 : return false;
3484 : }
3485 :
3486 0 : return true;
3487 : }
3488 :
3489 :
3490 :
3491 : /** \fn void server::table_is_accessible(QString const & table_name, accessible_flag_t & accessible)
3492 : * \brief Check whether a table can securily be used in a script.
3493 : *
3494 : * This signal is sent by the cell() function of snap_expr objects.
3495 : * The plugins receiving the signal can check the table name
3496 : * and mark it as accessible or secure.
3497 : *
3498 : * A table only marked as accessible can be accessed safely.
3499 : *
3500 : * \code
3501 : * void my_plugin::on_table_isaccessible(QString const & table_name, accessible_flag_t & accessible)
3502 : * {
3503 : * if(table_name == get_name(name_t::SNAP_NAME_MYPLUGIN_TABLE_NAME))
3504 : * {
3505 : * accessible.mark_as_accessible();
3506 : * }
3507 : * }
3508 : * \endcode
3509 : *
3510 : * It is possible for a plugin to mark certain tables as not accessible,
3511 : * whether or not a plugin mark them as accessible. For example:
3512 : *
3513 : * \code
3514 : * void content::on_table_isaccessible(QString const & table_name, accessible_flag_t & accessible)
3515 : * {
3516 : * if(table_name == get_name(name_t::SNAP_NAME_CONTENT_SECRET_TABLE))
3517 : * {
3518 : * // explicitly mark this table as a secure table
3519 : * accessible.mark_as_secure();
3520 : * }
3521 : * }
3522 : * \endcode
3523 : *
3524 : * This is used, for example, to protect the users and secret tables.
3525 : * Even though passwords are encrypted, allowing an end user to get a copy
3526 : * of the encrypted password would dearly simplify the work of a hacker in
3527 : * finding the unencrypted password.
3528 : *
3529 : * The \p secure flag is used to mark the cell as secure. Simply call
3530 : * the mark_as_secure() function to do so. This means the table cannot
3531 : * be accessed and the cell() function fails.
3532 : *
3533 : * \param[in] table The table being accessed.
3534 : * \param[in] accessible Whether the cell is secure.
3535 : */
3536 :
3537 :
3538 : /** \fn void server::add_snap_expr_functions(snap_expr::functions_t& functions)
3539 : * \brief Give a change to different plugins to add functions for snap_expr.
3540 : *
3541 : * This function gives a chance to any plugin listening to this signal
3542 : * to add functions that the snap_expr can then make use of.
3543 : *
3544 : * \return true in case the signal is to be broadcast.
3545 : */
3546 :
3547 :
3548 : /** \fn void server::output_result(QString const& uri_path, QByteArray& result)
3549 : * \brief Implementation of the output_result signal.
3550 : *
3551 : * The output_result() signal offers the result buffer to all the plugins
3552 : * to look at. Since the buffer is passed as a reference, a plugin can
3553 : * modify it as required although it is not generally expected to happen.
3554 : *
3555 : * It may also be used to process the result and exit if a plugin thinks
3556 : * that the default processing is not going to be capable of handling
3557 : * the data appropriately. For example, the server_access plugin
3558 : * intercepts all results and transforms them to an AJAX response in
3559 : * case the request was an AJAX request.
3560 : *
3561 : * \param[in,out] result The result buffer.
3562 : *
3563 : * \return true if the signal has to be sent to other plugins.
3564 : */
3565 :
3566 :
3567 :
3568 : /** \brief Initializes a quiet error callback object.
3569 : *
3570 : * This function initializes an error callback object. It expects a pointer
3571 : * to the running snap_child.
3572 : *
3573 : * The \p log parameter is used to know whether the errors and redirects
3574 : * should be logged or not. In most cases it probably will be set to
3575 : * false to avoid large amounts of logs.
3576 : *
3577 : * \param[in,out] snap The snap pointer.
3578 : * \param[in] log The log flag, if true send all errors to the loggers.
3579 : */
3580 0 : quiet_error_callback::quiet_error_callback(snap_child * snap, bool log)
3581 : : f_snap(snap)
3582 0 : , f_log(log)
3583 : //, f_error(false) -- auto-init
3584 : {
3585 0 : }
3586 :
3587 :
3588 : /** \brief Generate an error.
3589 : *
3590 : * This function is called when the user is trying to view something that
3591 : * is not accessible. The system already checked to know whether the user
3592 : * could upgrade to a higher level of control and failed, so the user
3593 : * simply cannot access this page. Hence we do not try to redirect him to
3594 : * a log in screen, and instead generate an error.
3595 : *
3596 : * In this default implementation, we simply log the information (assuming
3597 : * the object was created with the log flag set to true) and mark the
3598 : * object as erroneous.
3599 : *
3600 : * \param[in] err_code The HTTP code to be returned to the user.
3601 : * \param[in] err_name The name of the error being generated.
3602 : * \param[in] err_description A more complete description of the error.
3603 : * \param[in] err_details The internal details about the error (for system administrators only).
3604 : * \param[in] err_by_mime_type If returning an error, do not return HTML when this element MIME type is something else, instead send a file of that type, but still with the HTTP error code supplied.
3605 : */
3606 0 : void quiet_error_callback::on_error(snap_child::http_code_t const err_code, QString const & err_name, QString const & err_description, QString const & err_details, bool const err_by_mime_type)
3607 : {
3608 : // since we ignore the error here anyway we can ignore this flag...
3609 0 : NOTUSED(err_by_mime_type);
3610 :
3611 0 : f_error = true;
3612 :
3613 0 : if(f_log)
3614 : {
3615 : // log the error so administrators know something happened
3616 0 : SNAP_LOG_ERROR("error #")(static_cast<int>(err_code))(":")(err_name)(": ")(err_description)(" -- ")(err_details);
3617 : }
3618 0 : }
3619 :
3620 :
3621 : /** \brief Redirect the user so he can log in.
3622 : *
3623 : * In most cases this function is used to redirect the user to a log in page.
3624 : * It may be a log in screen to escalate the user to a new level so he can
3625 : * authorize changes requiring a higher level of control.
3626 : *
3627 : * In the base implementation, the error is logged (assuming the object was
3628 : * created with the log flag set to true) and the object is marked as
3629 : * erroneous, meaning that the object being checked will remain hidden.
3630 : * However, the user does not get redirected.
3631 : *
3632 : * \param[in] err_name The name of the error being generated.
3633 : * \param[in] err_description A more complete description of the error.
3634 : * \param[in] err_details The internal details about the error (for system administrators only).
3635 : * \param[in] err_security Whether the error is a security error (cannot be displayed to the end user).
3636 : * \param[in] path The path that generated the error.
3637 : * \param[in] http_code The HTTP code to be returned to the user.
3638 : */
3639 0 : void quiet_error_callback::on_redirect(
3640 : /* message::set_error() */ QString const & err_name, QString const & err_description, QString const & err_details, bool err_security,
3641 : /* snap_child::page_redirect() */ QString const & path, snap_child::http_code_t const http_code)
3642 : {
3643 0 : NOTUSED(err_security);
3644 :
3645 0 : f_error = true;
3646 0 : if(f_log)
3647 : {
3648 : // log the feat so administrators know something happened
3649 0 : SNAP_LOG_ERROR("error #")(static_cast<int>(http_code))(":")(err_name)(": ")(err_description)(" -- ")(err_details)(" (would redirect to: \"")(path)("\")");
3650 : }
3651 0 : }
3652 :
3653 :
3654 : /** \brief Clear the error.
3655 : *
3656 : * This function clear the error flag.
3657 : *
3658 : * This class is often used in a loop such as the one used to generate all
3659 : * the boxes on a page. The same object can be reused to check
3660 : * wehther a box is accessible or not, however, the object needs to clear
3661 : * its state before you test another box or all the boxes after the first
3662 : * that's currently forbidden would get hidden.
3663 : */
3664 0 : void quiet_error_callback::clear_error()
3665 : {
3666 0 : f_error = false;
3667 0 : }
3668 :
3669 :
3670 : /** \brief Check whether an error occurred.
3671 : *
3672 : * This function returns true if one of the on_redirect() or on_error()
3673 : * function were called during the process. If so, then the page is
3674 : * protected.
3675 : *
3676 : * In most cases the redirect is used to send the user to the log in screen.
3677 : * If the user is on a page that proves he cannot have an account or is
3678 : * already logged in and he cannot increase his rights, then the on_error()
3679 : * function is used. So in effect, either function represents the same
3680 : * thing: the user cannot access the specified page.
3681 : *
3682 : * \return true if an error occurred and thus the page is not accessible.
3683 : */
3684 0 : bool quiet_error_callback::has_error() const
3685 : {
3686 0 : return f_error;
3687 : }
3688 :
3689 :
3690 : /** \brief Add an action to the specified action set.
3691 : *
3692 : * This function adds an action to this action set.
3693 : *
3694 : * The action name must be unique within a plugin. The function
3695 : * forces the name of the plugin as a namespace so the name ends
3696 : * up looking something like this (for the "reset" action of
3697 : * the "list" plugin):
3698 : *
3699 : * \code
3700 : * list::reset
3701 : * \endcode
3702 : *
3703 : * \exception snapwebsites_exception_invalid_parameters
3704 : * If the plugin does not implement the backend_action, then this
3705 : * exception is raised. It should happen rarely since without
3706 : * implementing that interface you end up never receiving the
3707 : * event. That being said, if you implement a function and forget
3708 : * to add the derivation, it will compile and raise this exception.
3709 : *
3710 : * \param[in] action The action to be added.
3711 : * \param[in] p The plugin that is registering this \p action.
3712 : */
3713 0 : void server::backend_action_set::add_action(QString const & action, plugin * p)
3714 : {
3715 : // make sure that this plugin implements the backend action
3716 : //
3717 0 : backend_action * ba(dynamic_cast<backend_action *>(p));
3718 0 : if(ba == nullptr)
3719 : {
3720 : throw snapwebsites_exception_invalid_parameters("snapwebsites.cpp:"
3721 0 : " could not cast p to a backend_action pointer");
3722 : }
3723 :
3724 : // calculate the full name of this action
3725 : //
3726 0 : QString const name(QString("%1::%2").arg(p->get_plugin_name()).arg(action));
3727 :
3728 : // make sure we do not get duplicates
3729 : //
3730 0 : if(f_actions.contains(name))
3731 : {
3732 : throw snapwebsites_exception_invalid_parameters(
3733 0 : QString("snapwebsites.cpp: server::backend_action_set::add_action()"
3734 0 : " was called with \"%1\" twice.").arg(name));
3735 : }
3736 :
3737 0 : SNAP_LOG_DEBUG("adding action \"")(name)("\".");
3738 0 : f_actions[name] = ba;
3739 0 : }
3740 :
3741 :
3742 : /** \brief Check whether a named action is defined in this set.
3743 : *
3744 : * Note that various websites may have various actions registered
3745 : * depending on which plugin is installed. This function is used
3746 : * to know whether an action is defined for that website.
3747 : *
3748 : * \note
3749 : * The backend processing function exits with an error when an
3750 : * action is not defined. This does not prevent the process from
3751 : * moving forward (since the same action is generally run against
3752 : * all the installed websites.)
3753 : *
3754 : * \param[in] action The action to check the existance of.
3755 : *
3756 : * \return true if a plugin defines that specific action.
3757 : */
3758 0 : bool server::backend_action_set::has_action(QString const & action) const
3759 : {
3760 0 : return f_actions.contains(action);
3761 : }
3762 :
3763 :
3764 : /** \brief Actually call the backend action function.
3765 : *
3766 : * This function calls the plugin implementation of the on_backend_action()
3767 : * function.
3768 : *
3769 : * The function is passed the \p action parameter since the same function
3770 : * may get called for any number of actions (depending on how many where
3771 : * recorded.)
3772 : *
3773 : * \warning
3774 : * Note that CRON and non-CRON actions are both executed the same way.
3775 : * The plugin is aware of which action was registered as a CRON action
3776 : * and which was registered as a non-CRON action.
3777 : *
3778 : * \param[in] action The action being executed.
3779 : */
3780 0 : void server::backend_action_set::execute_action(QString const & action)
3781 : {
3782 0 : if(has_action(action))
3783 : {
3784 : // the plugin itself expects the action name without the namespace
3785 : // so we remove it here before we run the callback
3786 : //
3787 0 : plugins::plugin * p(dynamic_cast<plugins::plugin *>(f_actions[action]));
3788 0 : if(p) // always expected to be defined
3789 : {
3790 0 : QString const namespace_name(p->get_plugin_name());
3791 : // the +2 is to skip the '::'
3792 0 : f_actions[action]->on_backend_action(action.mid(namespace_name.length() + 2));
3793 : }
3794 : }
3795 0 : }
3796 :
3797 :
3798 : /** \brief Retrieve the name of the plugin of a given action.
3799 : *
3800 : * This function retrieves the name of the plugin linked to a certain
3801 : * action.
3802 : *
3803 : * \param[in] action The action of which we are interested by the plugin.
3804 : *
3805 : * \return The name of the plugin, if the action is defined, otherwise "".
3806 : */
3807 0 : QString server::backend_action_set::get_plugin_name(QString const & action)
3808 : {
3809 0 : if(has_action(action))
3810 : {
3811 0 : plugins::plugin * p(dynamic_cast<plugins::plugin *>(f_actions[action]));
3812 0 : if(p) // always expected to be defined
3813 : {
3814 0 : return p->get_plugin_name();
3815 : }
3816 : }
3817 :
3818 0 : return QString();
3819 : }
3820 :
3821 :
3822 : /** \brief Display the list of actions.
3823 : *
3824 : * This function can be used to display a list of actions.
3825 : *
3826 : * \warning
3827 : * This function is definitely not re-entrant (although it can
3828 : * be called any number of times by the same thread with the
3829 : * same result.)
3830 : */
3831 0 : void server::backend_action_set::display()
3832 : {
3833 0 : f_actions["list"] = nullptr;
3834 0 : for(actions_map_t::const_iterator it(f_actions.begin()); it != f_actions.end(); ++it)
3835 : {
3836 0 : std::cout << " " << it.key() << std::endl;
3837 : }
3838 0 : f_actions.remove("list");
3839 0 : }
3840 :
3841 :
3842 6 : } // namespace snap
3843 : // vim: ts=4 sw=4 et
|