]> granicus.if.org Git - icinga2/blob - icinga-app/icinga.cpp
Fix incorrect log message
[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.hpp"
21 #include "config/configcompiler.hpp"
22 #include "config/configitembuilder.hpp"
23 #include "base/application.hpp"
24 #include "base/logger.hpp"
25 #include "base/timer.hpp"
26 #include "base/utility.hpp"
27 #include "base/exception.hpp"
28 #include "base/convert.hpp"
29 #include "base/scriptvariable.hpp"
30 #include "base/context.hpp"
31 #include "base/clicommand.hpp"
32 #include "config.h"
33 #include <boost/program_options.hpp>
34 #include <boost/tuple/tuple.hpp>
35 #include <boost/foreach.hpp>
36
37 #ifndef _WIN32
38 #       include <sys/types.h>
39 #       include <pwd.h>
40 #       include <grp.h>
41 #endif /* _WIN32 */
42
43 using namespace icinga;
44 namespace po = boost::program_options;
45
46 #ifdef _WIN32
47 SERVICE_STATUS l_SvcStatus;
48 SERVICE_STATUS_HANDLE l_SvcStatusHandle;
49 #endif /* _WIN32 */
50
51 int Main(void)
52 {
53         int argc = Application::GetArgC();
54         char **argv = Application::GetArgV();
55
56         bool autocomplete = false;
57         int autoindex = 0;
58
59         if (argc >= 4 && strcmp(argv[1], "--autocomplete") == 0) {
60                 autocomplete = true;
61                 autoindex = Convert::ToLong(argv[2]);
62                 argc -= 3;
63                 argv += 3;
64         }
65
66         Application::SetStartTime(Utility::GetTime());
67
68         if (!autocomplete)
69                 Application::SetResourceLimits();
70
71         /* Set thread title. */
72         Utility::SetThreadName("Main Thread", false);
73
74         /* Install exception handlers to make debugging easier. */
75         Application::InstallExceptionHandlers();
76
77 #ifdef _WIN32
78         bool builtinPaths = true;
79
80         HKEY hKey;
81         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Icinga Development Team\\ICINGA2", 0,
82             KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
83                 BYTE pvData[MAX_PATH];
84                 DWORD cbData = sizeof(pvData)-1;
85                 DWORD lType;
86                 if (RegQueryValueEx(hKey, NULL, NULL, &lType, pvData, &cbData) == ERROR_SUCCESS && lType == REG_SZ) {
87                         pvData[cbData] = '\0';
88
89                         String prefix = (char *)pvData;
90                         Application::DeclarePrefixDir(prefix);
91                         Application::DeclareSysconfDir(prefix + "\\etc");
92                         Application::DeclareRunDir(prefix + "\\var\\run");
93                         Application::DeclareLocalStateDir(prefix + "\\var");
94                         Application::DeclarePkgDataDir(prefix + "\\share\\icinga2");
95                         Application::DeclareIncludeConfDir(prefix + "\\share\\icinga2\\include");
96
97                         builtinPaths = false;
98                 }
99
100                 RegCloseKey(hKey);
101         }
102
103         if (builtinPaths) {
104                 Log(LogWarning, "icinga-app", "Registry key could not be read. Falling back to built-in paths.");
105
106 #endif /* _WIN32 */
107                 Application::DeclarePrefixDir(ICINGA_PREFIX);
108                 Application::DeclareSysconfDir(ICINGA_SYSCONFDIR);
109                 Application::DeclareRunDir(ICINGA_RUNDIR);
110                 Application::DeclareLocalStateDir(ICINGA_LOCALSTATEDIR);
111                 Application::DeclarePkgDataDir(ICINGA_PKGDATADIR);
112                 Application::DeclareIncludeConfDir(ICINGA_INCLUDECONFDIR);
113 #ifdef _WIN32
114         }
115 #endif /* _WIN32 */
116
117         Application::DeclareZonesDir(Application::GetSysconfDir() + "/icinga2/zones.d");
118         Application::DeclareApplicationType("icinga/IcingaApplication");
119         Application::DeclareRunAsUser(ICINGA_USER);
120         Application::DeclareRunAsGroup(ICINGA_GROUP);
121
122         LogSeverity logLevel = Logger::GetConsoleLogSeverity();
123         Logger::SetConsoleLogSeverity(LogWarning);
124         
125         Utility::LoadExtensionLibrary("cli");
126
127         po::options_description visibleDesc("Global options");
128
129         visibleDesc.add_options()
130                 ("help", "show this help message")
131                 ("version,V", "show version information")
132                 ("define,D", po::value<std::vector<std::string> >(), "define a constant")
133                 ("library,l", po::value<std::vector<std::string> >(), "load a library")
134                 ("include,I", po::value<std::vector<std::string> >(), "add include search directory")
135                 ("log-level,x", po::value<std::string>(), "specify the log level for the console log")
136 #ifndef _WIN32
137                 ("user,u", po::value<std::string>(), "user to run Icinga as")
138                 ("group,g", po::value<std::string>(), "group to run Icinga as")
139 #endif /* _WIN32 */
140         ;
141
142         po::options_description hiddenDesc("Hidden options");
143
144         hiddenDesc.add_options()
145                 ("no-stack-rlimit", "used internally, do not specify manually");
146         
147         String cmdname;
148         CLICommand::Ptr command;
149         po::variables_map vm;
150
151         try {
152                 CLICommand::ParseCommand(argc, argv, visibleDesc, hiddenDesc, vm, cmdname, command, autocomplete);
153         } catch (const std::exception& ex) {
154                 std::ostringstream msgbuf;
155                 msgbuf << "Error while parsing command-line options: " << ex.what();
156                 Log(LogCritical, "icinga-app", msgbuf.str());
157                 return EXIT_FAILURE;
158         }
159
160         String initconfig = Application::GetSysconfDir() + "/icinga2/init.conf";
161
162         if (Utility::PathExists(initconfig)) {
163                 ConfigCompilerContext::GetInstance()->Reset();
164                 ConfigCompiler::CompileFile(initconfig);
165         }
166         
167         if (vm.count("define")) {
168                 BOOST_FOREACH(const String& define, vm["define"].as<std::vector<std::string> >()) {
169                         String key, value;
170                         size_t pos = define.FindFirstOf('=');
171                         if (pos != String::NPos) {
172                                 key = define.SubStr(0, pos);
173                                 value = define.SubStr(pos + 1);
174                         } else {
175                                 key = define;
176                                 value = "1";
177                         }
178                         ScriptVariable::Set(key, value);
179                 }
180         }
181
182         if (vm.count("group"))
183                 ScriptVariable::Set("RunAsGroup", String(vm["group"].as<std::string>()));
184
185         if (vm.count("user"))
186                 ScriptVariable::Set("RunAsUser", String(vm["user"].as<std::string>()));
187
188 #ifndef _WIN32
189         String group = Application::GetRunAsGroup();
190
191         errno = 0;
192         struct group *gr = getgrnam(group.CStr());
193
194         if (!gr) {
195                 if (errno == 0) {
196                         std::ostringstream msgbuf;
197                         msgbuf << "Invalid group specified: " + group;
198                         Log(LogCritical, "cli",  msgbuf.str());
199                         return EXIT_FAILURE;
200                 } else {
201                         std::ostringstream msgbuf;
202                         msgbuf << "getgrnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
203                         Log(LogCritical, "cli",  msgbuf.str());
204                         return EXIT_FAILURE;
205                 }
206         }
207
208         if (getgid() != gr->gr_gid) {
209                 if (!vm.count("reload-internal") && setgroups(0, NULL) < 0) {
210                         std::ostringstream msgbuf;
211                         msgbuf << "setgroups() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
212                         Log(LogCritical, "cli",  msgbuf.str());
213                         return EXIT_FAILURE;
214                 }
215         
216                 if (setgid(gr->gr_gid) < 0) {
217                         std::ostringstream msgbuf;
218                         msgbuf << "setgid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
219                         Log(LogCritical, "cli",  msgbuf.str());
220                         return EXIT_FAILURE;
221                 }
222         }
223
224         String user = Application::GetRunAsUser();
225
226         errno = 0;
227         struct passwd *pw = getpwnam(user.CStr());
228
229         if (!pw) {
230                 if (errno == 0) {
231                         std::ostringstream msgbuf;
232                         msgbuf << "Invalid user specified: " + user;
233                         Log(LogCritical, "cli",  msgbuf.str());
234                         return EXIT_FAILURE;
235                 } else {
236                         std::ostringstream msgbuf;
237                         msgbuf << "getpwnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
238                         Log(LogCritical, "cli",  msgbuf.str());
239                         return EXIT_FAILURE;
240                 }
241         }
242
243         // also activate the additional groups the configured user is member of
244         if (getuid() != pw->pw_uid) {
245                 if (!vm.count("reload-internal") && initgroups(user.CStr(), pw->pw_gid) < 0) {
246                         std::ostringstream msgbuf;
247                         msgbuf << "initgroups() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
248                         Log(LogCritical, "cli",  msgbuf.str());
249                         return EXIT_FAILURE;
250                 }
251         
252                 if (setuid(pw->pw_uid) < 0) {
253                         std::ostringstream msgbuf;
254                         msgbuf << "setuid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
255                         Log(LogCritical, "cli",  msgbuf.str());
256                         return EXIT_FAILURE;
257                 }
258         }
259 #endif /* _WIN32 */
260
261         Application::DeclareStatePath(Application::GetLocalStateDir() + "/lib/icinga2/icinga2.state");
262         Application::DeclareObjectsPath(Application::GetLocalStateDir() + "/cache/icinga2/icinga2.debug");
263         Application::DeclarePidPath(Application::GetRunDir() + "/icinga2/icinga2.pid");
264
265         ConfigCompiler::AddIncludeSearchDir(Application::GetIncludeConfDir());
266
267         if (!autocomplete && vm.count("include")) {
268                 BOOST_FOREACH(const String& includePath, vm["include"].as<std::vector<std::string> >()) {
269                         ConfigCompiler::AddIncludeSearchDir(includePath);
270                 }
271         }
272         
273         Logger::SetConsoleLogSeverity(logLevel);
274
275         if (!autocomplete) {
276                 if (vm.count("log-level")) {
277                         String severity = vm["log-level"].as<std::string>();
278         
279                         LogSeverity logLevel = LogInformation;
280                         try {
281                                 logLevel = Logger::StringToSeverity(severity);
282                         } catch (std::exception&) {
283                                 /* use the default */
284                                 Log(LogWarning, "icinga", "Invalid log level set. Using default 'information'.");
285                         }
286         
287                         Logger::SetConsoleLogSeverity(logLevel);
288                 }
289         
290                 if (vm.count("library")) {
291                         BOOST_FOREACH(const String& libraryName, vm["library"].as<std::vector<std::string> >()) {
292                                 (void)Utility::LoadExtensionLibrary(libraryName);
293                         }
294                 }
295         
296                 if (!command || vm.count("help") || vm.count("version")) {
297                         String appName = Utility::BaseName(Application::GetArgV()[0]);
298         
299                         if (appName.GetLength() > 3 && appName.SubStr(0, 3) == "lt-")
300                                 appName = appName.SubStr(3, appName.GetLength() - 3);
301         
302                         std::cout << appName << " " << "- The Icinga 2 network monitoring daemon.";
303         
304                         if (!command || vm.count("help")) {
305                                 std::cout << std::endl << std::endl
306                                     << "Usage:" << std::endl
307                                     << "  " << argv[0] << " ";
308                                     
309                                 if (cmdname.IsEmpty())
310                                         std::cout << "<command>";
311                                 else
312                                         std::cout << cmdname;
313                                         
314                                 std::cout << " [<arguments>]";
315                                 
316                                 if (command) {
317                                         std::cout << std::endl << std::endl
318                                                   << command->GetDescription();
319                                 }
320                         }
321                         
322                         if (vm.count("version")) {
323                                 std::cout << " (Version: " << Application::GetVersion() << ")";
324                                 std::cout << std::endl
325                                         << "Copyright (c) 2012-2014 Icinga Development Team (http://www.icinga.org)" << std::endl
326                                         << "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl2.html>" << std::endl
327                                         << "This is free software: you are free to change and redistribute it." << std::endl
328                                         << "There is NO WARRANTY, to the extent permitted by law.";
329                         }
330         
331                         std::cout << std::endl;
332         
333                         if (vm.count("version")) {
334                                 std::cout << std::endl;
335         
336                                 Application::DisplayInfoMessage(true);
337         
338                                 return EXIT_SUCCESS;
339                         }
340                 }
341         
342                 if (!command || vm.count("help")) {
343                         if (!command) {
344                                 std::cout << std::endl;
345                                 CLICommand::ShowCommands(argc, argv, NULL);
346                         }
347         
348                         std::cout << std::endl
349                                 << visibleDesc << std::endl
350                                 << "Report bugs at <https://dev.icinga.org/>" << std::endl
351                                 << "Icinga home page: <http://www.icinga.org/>" << std::endl;
352                         return EXIT_SUCCESS;
353                 }
354         }
355
356         int rc = 1;
357
358         if (autocomplete) {
359                 CLICommand::ShowCommands(argc, argv, &visibleDesc, &hiddenDesc, true, autoindex);
360                 rc = 0;
361         } else if (command)
362                 rc = command->Run(vm);
363
364 #ifndef _DEBUG
365         Application::Exit(rc);
366 #endif /* _DEBUG */
367
368         return rc;
369 }
370
371 #ifdef _WIN32
372 static int SetupService(bool install, int argc, char **argv)
373 {
374         SC_HANDLE schSCManager = OpenSCManager(
375                 NULL,
376                 NULL,
377                 SC_MANAGER_ALL_ACCESS);
378
379         if (NULL == schSCManager) {
380                 printf("OpenSCManager failed (%d)\n", GetLastError());
381                 return 1;
382         }
383
384         TCHAR szPath[MAX_PATH];
385
386         if (!GetModuleFileName(NULL, szPath, MAX_PATH)) {
387                 printf("Cannot install service (%d)\n", GetLastError());
388                 return 1;
389         }
390
391         String szArgs;
392         szArgs = Utility::EscapeShellArg(szPath) + " --scm";
393
394         for (int i = 0; i < argc; i++)
395                 szArgs += " " + Utility::EscapeShellArg(argv[i]);
396
397         SC_HANDLE schService = OpenService(schSCManager, "icinga2", DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
398
399         if (schService != NULL) {
400                 SERVICE_STATUS status;
401                 ControlService(schService, SERVICE_CONTROL_STOP, &status);
402
403                 double start = Utility::GetTime();
404                 while (status.dwCurrentState != SERVICE_STOPPED) {
405                         double end = Utility::GetTime();
406
407                         if (end - start > 30) {
408                                 printf("Could not stop the service.\n");
409                                 break;
410                         }
411
412                         Utility::Sleep(5);
413
414                         if (!QueryServiceStatus(schService, &status)) {
415                                 printf("QueryServiceStatus failed (%d)\n", GetLastError());
416                                 return 1;
417                         }
418                 }
419
420                 if (!DeleteService(schService)) {
421                         printf("DeleteService failed (%d)\n", GetLastError());
422                         CloseServiceHandle(schService);
423                         CloseServiceHandle(schSCManager);
424                         return 1;
425                 }
426
427                 if (!install)
428                         printf("Service uninstalled successfully\n");
429
430                 CloseServiceHandle(schService);
431         }
432
433         if (install) {
434                 schService = CreateService(
435                         schSCManager,
436                         "icinga2",
437                         "Icinga 2",
438                         SERVICE_ALL_ACCESS,
439                         SERVICE_WIN32_OWN_PROCESS,
440                         SERVICE_DEMAND_START,
441                         SERVICE_ERROR_NORMAL,
442                         szArgs.CStr(),
443                         NULL,
444                         NULL,
445                         NULL,
446                         "NT AUTHORITY\\NetworkService",
447                         NULL);
448
449                 if (schService == NULL) {
450                         printf("CreateService failed (%d)\n", GetLastError());
451                         CloseServiceHandle(schSCManager);
452                         return 1;
453                 } else
454                         printf("Service installed successfully\n");
455
456                 ChangeServiceConfig(schService, SERVICE_NO_CHANGE, SERVICE_AUTO_START,
457                     SERVICE_ERROR_NORMAL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
458
459                 SERVICE_DESCRIPTION sdDescription = { "The Icinga 2 monitoring application" };
460                 ChangeServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, &sdDescription);
461
462                 if (!StartService(schService, 0, NULL)) {
463                         printf("StartService failed (%d)\n", GetLastError());
464                         CloseServiceHandle(schService);
465                         CloseServiceHandle(schSCManager);
466                         return 1;
467                 }
468
469                 CloseServiceHandle(schService);
470         }
471
472         CloseServiceHandle(schSCManager);
473
474         return 0;
475 }
476
477 VOID ReportSvcStatus(DWORD dwCurrentState,
478         DWORD dwWin32ExitCode,
479         DWORD dwWaitHint)
480 {
481         static DWORD dwCheckPoint = 1;
482
483         l_SvcStatus.dwCurrentState = dwCurrentState;
484         l_SvcStatus.dwWin32ExitCode = dwWin32ExitCode;
485         l_SvcStatus.dwWaitHint = dwWaitHint;
486
487         if (dwCurrentState == SERVICE_START_PENDING)
488                 l_SvcStatus.dwControlsAccepted = 0;
489         else
490                 l_SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
491
492         if ((dwCurrentState == SERVICE_RUNNING) ||
493             (dwCurrentState == SERVICE_STOPPED))
494                 l_SvcStatus.dwCheckPoint = 0;
495         else
496                 l_SvcStatus.dwCheckPoint = dwCheckPoint++;
497
498         SetServiceStatus(l_SvcStatusHandle, &l_SvcStatus);
499 }
500
501 VOID WINAPI ServiceControlHandler(DWORD dwCtrl)
502 {
503         if (dwCtrl == SERVICE_CONTROL_STOP) {
504                 ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
505                 Application::RequestShutdown();
506         }
507 }
508
509 VOID WINAPI ServiceMain(DWORD argc, LPSTR *argv)
510 {
511         l_SvcStatusHandle = RegisterServiceCtrlHandler(
512                 "icinga2",
513                 ServiceControlHandler);
514
515         l_SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
516         l_SvcStatus.dwServiceSpecificExitCode = 0;
517
518         ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
519
520         int rc = Main();
521
522         ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, rc);
523 }
524 #endif /* _WIN32 */
525
526 /**
527 * Entry point for the Icinga application.
528 *
529 * @params argc Number of command line arguments.
530 * @params argv Command line arguments.
531 * @returns The application's exit status.
532 */
533 int main(int argc, char **argv)
534 {
535         /* must be called before using any other libbase functions */
536         Application::InitializeBase();
537
538         /* Set command-line arguments. */
539         Application::SetArgC(argc);
540         Application::SetArgV(argv);
541
542 #ifdef _WIN32
543         if (argc > 1 && strcmp(argv[1], "--scm-install") == 0) {
544                 return SetupService(true, argc - 2, &argv[2]);
545         }
546
547         if (argc > 1 && strcmp(argv[1], "--scm-uninstall") == 0) {
548                 return SetupService(false, argc - 2, &argv[2]);
549         }
550
551         if (argc > 1 && strcmp(argv[1], "--scm") == 0) {
552                 SERVICE_TABLE_ENTRY dispatchTable[] = {
553                         { "icinga2", ServiceMain },
554                         { NULL, NULL }
555                 };
556
557                 StartServiceCtrlDispatcher(dispatchTable);
558                 Application::Exit(1);
559         }
560 #endif /* _WIN32 */
561
562         int rc = Main();
563
564         Application::Exit(rc);
565 }