]> granicus.if.org Git - icinga2/blob - icinga-app/icinga.cpp
Update "swap" check command.
[icinga2] / icinga-app / icinga.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)    *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "config/configcompilercontext.h"
21 #include "config/configcompiler.h"
22 #include "config/configitembuilder.h"
23 #include "base/application.h"
24 #include "base/logger.h"
25 #include "base/timer.h"
26 #include "base/utility.h"
27 #include "base/exception.h"
28 #include "base/convert.h"
29 #include "base/scriptvariable.h"
30 #include "base/context.h"
31 #include "config.h"
32 #include <boost/program_options.hpp>
33 #include <boost/tuple/tuple.hpp>
34 #include <boost/foreach.hpp>
35
36 #ifndef _WIN32
37 #       include <sys/types.h>
38 #       include <pwd.h>
39 #       include <grp.h>
40 #endif /* _WIN32 */
41
42 using namespace icinga;
43 namespace po = boost::program_options;
44
45 static po::variables_map g_AppParams;
46
47 static String LoadAppType(const String& typeSpec)
48 {
49         int index;
50
51         Log(LogInformation, "icinga-app", "Loading application type: " + typeSpec);
52
53         index = typeSpec.FindFirstOf('/');
54
55         if (index == String::NPos)
56                 return typeSpec;
57
58         String library = typeSpec.SubStr(0, index);
59
60         (void) Utility::LoadExtensionLibrary(library);
61
62         return typeSpec.SubStr(index + 1);
63 }
64
65 static bool LoadConfigFiles(const String& appType, ValidationType validate)
66 {
67         ConfigCompilerContext::GetInstance()->Reset();
68
69         if (g_AppParams.count("config") > 0) {
70                 BOOST_FOREACH(const String& configPath, g_AppParams["config"].as<std::vector<std::string> >()) {
71                         ConfigCompiler::CompileFile(configPath);
72                 }
73         }
74
75         String name, fragment;
76         BOOST_FOREACH(boost::tie(name, fragment), ConfigFragmentRegistry::GetInstance()->GetItems()) {
77                 ConfigCompiler::CompileText(name, fragment);
78         }
79
80         ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>();
81         builder->SetType(appType);
82         builder->SetName("application");
83         ConfigItem::Ptr item = builder->Compile();
84         item->Register();
85
86         bool result = ConfigItem::ActivateItems(validate);
87
88         int warnings = 0, errors = 0;
89
90         BOOST_FOREACH(const ConfigCompilerMessage& message, ConfigCompilerContext::GetInstance()->GetMessages()) {
91                 std::ostringstream locbuf;
92                 ShowCodeFragment(locbuf, message.Location, true);
93                 String location = locbuf.str();
94
95                 String logmsg;
96
97                 if (!location.IsEmpty())
98                         logmsg = "Location:\n" + location;
99
100                 logmsg += String("\nConfig ") + (message.Error ? "error" : "warning") + ": " + message.Text;
101
102                 if (message.Error) {
103                         Log(LogCritical, "config", logmsg);
104                         errors++;
105                 } else {
106                         Log(LogWarning, "config", logmsg);
107                         warnings++;
108                 }
109         }
110
111         if (warnings > 0 || errors > 0) {
112                 LogSeverity severity;
113
114                 if (errors == 0)
115                         severity = LogWarning;
116                 else
117                         severity = LogCritical;
118
119                 Log(severity, "config", Convert::ToString(errors) + " errors, " + Convert::ToString(warnings) + " warnings.");
120         }
121
122         if (!result)
123                 return false;
124
125         return true;
126 }
127
128 #ifndef _WIN32
129 static void SigHupHandler(int)
130 {
131         Application::RequestRestart();
132 }
133 #endif /* _WIN32 */
134
135 static bool Daemonize(const String& stderrFile)
136 {
137 #ifndef _WIN32
138         pid_t pid = fork();
139         if (pid == -1) {
140                 return false;
141         }
142
143         if (pid)
144                 exit(0);
145
146         int fdnull = open("/dev/null", O_RDWR);
147         if (fdnull > 0) {
148                 if (fdnull != 0)
149                         dup2(fdnull, 0);
150
151                 if (fdnull != 1)
152                         dup2(fdnull, 1);
153
154                 if (fdnull > 2)
155                         close(fdnull);
156         }
157
158         const char *errPath = "/dev/null";
159
160         if (!stderrFile.IsEmpty())
161                 errPath = stderrFile.CStr();
162
163         int fderr = open(errPath, O_WRONLY | O_APPEND);
164
165         if (fderr < 0 && errno == ENOENT)
166                 fderr = open(errPath, O_CREAT | O_WRONLY | O_APPEND, 0600);
167
168         if (fderr > 0) {
169                 if (fderr != 2)
170                         dup2(fderr, 2);
171
172                 if (fderr > 2)
173                         close(fderr);
174         }
175
176         pid_t sid = setsid();
177         if (sid == -1) {
178                 return false;
179         }
180 #endif
181
182         return true;
183 }
184
185 /**
186  * Entry point for the Icinga application.
187  *
188  * @params argc Number of command line arguments.
189  * @params argv Command line arguments.
190  * @returns The application's exit status.
191  */
192 int main(int argc, char **argv)
193 {
194         Application::SetStartTime(Utility::GetTime());
195
196         Application::SetResourceLimits();
197
198         /* Set thread title. */
199         Utility::SetThreadName("Main Thread", false);
200
201         /* Set command-line arguments. */
202         Application::SetArgC(argc);
203         Application::SetArgV(argv);
204
205         /* Install exception handlers to make debugging easier. */
206         Application::InstallExceptionHandlers();
207
208         Application::DeclarePrefixDir(ICINGA_PREFIX);
209         Application::DeclareSysconfDir(ICINGA_SYSCONFDIR);
210         Application::DeclareLocalStateDir(ICINGA_LOCALSTATEDIR);
211         Application::DeclarePkgDataDir(ICINGA_PKGDATADIR);
212
213         Application::DeclareApplicationType("icinga/IcingaApplication");
214
215         po::options_description desc("Supported options");
216         desc.add_options()
217                 ("help", "show this help message")
218                 ("version,V", "show version information")
219                 ("library,l", po::value<std::vector<std::string> >(), "load a library")
220                 ("include,I", po::value<std::vector<std::string> >(), "add include search directory")
221                 ("define,D", po::value<std::vector<std::string> >(), "define a constant")
222                 ("config,c", po::value<std::vector<std::string> >(), "parse a configuration file")
223                 ("no-config,z", "start without a configuration file")
224                 ("validate,C", "exit after validating the configuration")
225                 ("no-validate,Z", "skip validating the configuration")
226                 ("debug,x", "enable debugging")
227                 ("daemonize,d", "detach from the controlling terminal")
228                 ("errorlog,e", po::value<std::string>(), "log fatal errors to the specified log file (only works in combination with --daemonize)")
229 #ifndef _WIN32
230                 ("user,u", po::value<std::string>(), "user to run Icinga as")
231                 ("group,g", po::value<std::string>(), "group to run Icinga as")
232 #endif
233         ;
234
235         try {
236                 po::store(po::parse_command_line(argc, argv, desc), g_AppParams);
237         } catch (const std::exception& ex) {
238                 std::ostringstream msgbuf;
239                 msgbuf << "Error while parsing command-line options: " << ex.what();
240                 Log(LogCritical, "icinga-app", msgbuf.str());
241                 return EXIT_FAILURE;
242         }
243
244         po::notify(g_AppParams);
245
246 #ifndef _WIN32
247         if (g_AppParams.count("group")) {
248                 String group = g_AppParams["group"].as<std::string>();
249
250                 errno = 0;
251                 struct group *gr = getgrnam(group.CStr());
252
253                 if (!gr) {
254                         if (errno == 0) {
255                                 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid group specified: " + group));
256                         } else {
257                                 BOOST_THROW_EXCEPTION(posix_error()
258                                         << boost::errinfo_api_function("getgrnam")
259                                         << boost::errinfo_errno(errno));
260                         }
261                 }
262
263                 if (setgid(gr->gr_gid) < 0) {
264                         BOOST_THROW_EXCEPTION(posix_error()
265                                 << boost::errinfo_api_function("setgid")
266                                 << boost::errinfo_errno(errno));
267                 }
268         }
269
270         if (g_AppParams.count("user")) {
271                 String user = g_AppParams["user"].as<std::string>();
272
273                 errno = 0;
274                 struct passwd *pw = getpwnam(user.CStr());
275
276                 if (!pw) {
277                         if (errno == 0) {
278                                 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid user specified: " + user));
279                         } else {
280                                 BOOST_THROW_EXCEPTION(posix_error()
281                                         << boost::errinfo_api_function("getpwnam")
282                                         << boost::errinfo_errno(errno));
283                         }
284                 }
285
286                 if (setuid(pw->pw_uid) < 0) {
287                         BOOST_THROW_EXCEPTION(posix_error()
288                                 << boost::errinfo_api_function("setuid")
289                                 << boost::errinfo_errno(errno));
290                 }
291         }
292 #endif /* _WIN32 */
293
294         if (g_AppParams.count("debug"))
295                 Application::SetDebugging(true);
296
297         if (g_AppParams.count("help") || g_AppParams.count("version")) {
298                 String appName = Utility::BaseName(argv[0]);
299
300                 if (appName.GetLength() > 3 && appName.SubStr(0, 3) == "lt-")
301                         appName = appName.SubStr(3, appName.GetLength() - 3);
302
303                 std::cout << appName << " " << "- The Icinga 2 network monitoring daemon.";
304
305                 if (g_AppParams.count("version")) {
306                         std::cout  << " (Version: " << Application::GetVersion() << ")";
307                         std::cout << std::endl
308                                   << "Copyright (c) 2012-2014 Icinga Development Team (http://www.icinga.org)" << std::endl
309                                   << "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl2.html>" << std::endl
310                                   << "This is free software: you are free to change and redistribute it." << std::endl
311                                   << "There is NO WARRANTY, to the extent permitted by law.";
312                 }
313
314                 std::cout << std::endl;
315
316                 if (g_AppParams.count("version"))
317                         return EXIT_SUCCESS;
318         }
319
320         if (g_AppParams.count("help")) {
321                 std::cout << std::endl
322                           << desc << std::endl
323                           << "Report bugs at <https://dev.icinga.org/>" << std::endl
324                           << "Icinga home page: <http://www.icinga.org/>" << std::endl;
325                 return EXIT_SUCCESS;
326         }
327
328         if (g_AppParams.count("define")) {
329                 BOOST_FOREACH(const String& define, g_AppParams["define"].as<std::vector<std::string> >()) {
330                         String key, value;
331                         size_t pos = define.FindFirstOf('=');
332                         if (pos != String::NPos) {
333                                 key = define.SubStr(0, pos);
334                                 value = define.SubStr(pos + 1);
335                         }
336                         else {
337                                 key = define;
338                                 value = "1";
339                         }
340                         ScriptVariable::Set(key, value);
341                 }
342         }
343
344         Application::DeclareStatePath(Application::GetLocalStateDir() + "/lib/icinga2/icinga2.state");
345         Application::DeclarePidPath(Application::GetLocalStateDir() + "/run/icinga2/icinga2.pid");
346
347         ScriptVariable::Set("UseVfork", true, false, true);
348
349         Application::MakeVariablesConstant();
350
351         Log(LogInformation, "icinga-app", "Icinga application loader (version: " + Application::GetVersion() + ")");
352
353         String appType = LoadAppType(Application::GetApplicationType());
354
355         if (g_AppParams.count("library")) {
356                 BOOST_FOREACH(const String& libraryName, g_AppParams["library"].as<std::vector<std::string> >()) {
357                         (void) Utility::LoadExtensionLibrary(libraryName);
358                 }
359         }
360
361         ConfigCompiler::AddIncludeSearchDir(Application::GetPkgDataDir());
362
363         if (g_AppParams.count("include")) {
364                 BOOST_FOREACH(const String& includePath, g_AppParams["include"].as<std::vector<std::string> >()) {
365                         ConfigCompiler::AddIncludeSearchDir(includePath);
366                 }
367         }
368
369         if (g_AppParams.count("no-config") == 0 && g_AppParams.count("config") == 0) {
370                 Log(LogCritical, "icinga-app", "You need to specify at least one config file (using the --config option).");
371
372                 return EXIT_FAILURE;
373         }
374
375         if (g_AppParams.count("daemonize")) {
376                 String errorLog;
377
378                 if (g_AppParams.count("errorlog"))
379                         errorLog = g_AppParams["errorlog"].as<std::string>();
380
381                 Daemonize(errorLog);
382                 Logger::DisableConsoleLog();
383         }
384
385         ValidationType validate = ValidateStart;
386
387         if(g_AppParams.count("validate"))
388                 validate = ValidateOnly;
389
390         if(g_AppParams.count("no-validate"))
391                 validate = ValidateNone;
392
393         if (!LoadConfigFiles(appType, validate))
394                 return EXIT_FAILURE;
395
396         if (validate == ValidateOnly) {
397                 Log(LogInformation, "icinga-app", "Finished validating the configuration file(s).");
398                 return EXIT_SUCCESS;
399         }
400
401 #ifndef _WIN32
402         struct sigaction sa;
403         memset(&sa, 0, sizeof(sa));
404         sa.sa_handler = &SigHupHandler;
405         sigaction(SIGHUP, &sa, NULL);
406 #endif /* _WIN32 */
407
408         int rc = Application::GetInstance()->Run();
409
410 #ifdef _DEBUG
411         exit(rc);
412 #else /* _DEBUG */
413         _exit(rc); // Yay, our static destructors are pretty much beyond repair at this point.
414 #endif /* _DEBUG */
415 }