source: nscp/service/cli_parser.hpp @ 53be5c8

0.4.10.4.2
Last change on this file since 53be5c8 was 53be5c8, checked in by Michael Medin <michael@…>, 11 months ago
  • LUAScript: Improved lua scripting module a lot
  • LUAScript: Added protocol buffer support to lua scripts
  • tests: Rewrote (halfway there) test_nrpe python script as a lua script.
  • CheckEventLog: Fixed command name when submitting real-time "no action checks"
  • Property mode set to 100644
File size: 20.2 KB
Line 
1#pragma once
2
3#include <boost/program_options.hpp>
4#include "settings_client.hpp"
5#include <nsclient/logger.hpp>
6
7namespace po = boost::program_options;
8class cli_parser {
9       
10
11        NSClient* core_;
12        po::options_description root;
13        po::options_description common;
14        po::options_description settings;
15        po::options_description service;
16        po::options_description client;
17        po::options_description unittest;
18        po::options_description test;
19
20        bool help;
21        bool version;
22        bool log_debug;
23        std::wstring log_level;
24        std::wstring settings_store;
25
26        static nsclient::logging::logger_interface* get_logger() {
27                return nsclient::logging::logger::get_logger();
28        }
29
30public:
31        cli_parser(NSClient* core)
32                : core_(core)
33                , root("Allowed first option (Mode of operation)")
34                , common("Common options")
35                , settings("Settings options")
36                , service("Service Options")
37                , client("Client Options")
38                , unittest("Unit-test Options")
39                , test("Test Options")
40                , help(false)
41                , version(false)
42                , log_debug(false)
43        {
44                root.add_options()
45                        ("help", po::bool_switch(&help), "produce help message")
46                        ("version", po::bool_switch(&version), "Show version information")
47                        ;
48
49                common.add_options()
50                        ("settings", po::value<std::wstring>(&settings_store), "Override (temporarily) settings subsystem to use")
51                        ("help", po::bool_switch(&help), "produce help message")
52                        ("debug", po::bool_switch(&log_debug), "Set log level to debug (and show debug information)")
53                        ("log", po::value<std::wstring>(&log_level), "The log level to use")
54                        ("version", po::bool_switch(&version), "Show version information")
55                        ;
56
57                settings.add_options()
58                        ("migrate-to", po::value<std::wstring>(), "Migrate (copy) settings from current store to target store")
59                        ("migrate-from", po::value<std::wstring>(), "Migrate (copy) settings from current store to target store")
60                        ("generate", po::value<std::wstring>(), "(re)Generate a commented settings store or similar KEY can be trac, settings or the target store.")
61                        ("add-defaults", "Add all default (if missing) values.")
62                        ("load-all", "Load all plugins (currently only used with generate).")
63                        ("path", po::value<std::wstring>()->default_value(_T("")), "Path of key to work with.")
64                        ("key", po::value<std::wstring>()->default_value(_T("")), "Key to work with.")
65                        ("set", po::value<std::wstring>()->implicit_value(_T("")), "Set a key and path to a given value.")
66                        ("switch", po::value<std::wstring>(), "Set default context to use (similar to migrate but does NOT copy values)")
67                        ("show", "Set a value given a key and path.")
68                        ("list", "Set all keys below the path (or root).")
69                        ;
70
71                service.add_options()
72                        ("install", "Install service")
73                        ("uninstall", "Uninstall service")
74                        ("start", "Start service")
75                        ("stop", "Stop service")
76                        ("info", "Show information about service")
77                        ("run", "Run as a service")
78                        ("name", po::value<std::wstring>(), "Name of service")
79                        ("description", po::value<std::wstring>()->default_value(_T("")), "Description of service")
80                        ;
81
82                client.add_options()
83                        ("exec,e", po::value<std::wstring>()->implicit_value(_T("")), "Run a command (execute)")
84                        ("boot,b", "Boot the client before executing command (similar as running the command from test)")
85                        ("query,q", po::value<std::wstring>(), "Run a query with a given name")
86                        ("submit,s", po::value<std::wstring>(), "Name of query to ask")
87                        ("module,M", po::value<std::wstring>(), "Name of module to load (if not specified all modules in ini file will be loaded)")
88                        ("argument,a", po::wvalue<std::vector<std::wstring> >(), "List of arguments (gets -- prefixed automatically)")
89                        ("raw-argument", po::wvalue<std::vector<std::wstring> >(), "List of arguments (does not get -- prefixed)")
90                        ("load-all", "Load all plugins.")
91                        ;
92
93                unittest.add_options()
94                        ("language,l", po::value<std::wstring>()->implicit_value(_T("")), "Language tests are written in")
95                        ("argument,a", po::wvalue<std::vector<std::wstring> >(), "List of arguments (gets -- prefixed automatically)")
96                        ("raw-argument", po::wvalue<std::vector<std::wstring> >(), "List of arguments (does not get -- prefixed)")
97                        ;
98
99                test.add_options()
100                        ("log-to-file", "Enable file logger (defaults is console only)")
101                        ;
102
103        }
104
105        bool process_common_options(std::string context, po::options_description &desc) {
106                nsclient::logging::logger::get_logger()->set_console_log(true);
107                if (log_debug) {
108                        log_level = _T("debug");
109                }
110                if (!log_level.empty())
111                        nsclient::logging::logger::set_log_level(log_level);
112                if (!settings_store.empty())
113                        core_->set_settings_context(settings_store);
114
115                if (help) {
116                        std::cout << desc << std::endl;
117                        return true;
118                }
119                if (version) {
120                        std::cout << APPLICATION_NAME << _T(", Version: ") << CURRENT_SERVICE_VERSION << _T(", Platform: ") << SZARCH << std::endl;
121                        return true;
122                }
123                return false;
124        }
125
126        typedef boost::function<int(int, wchar_t**)> handler_function;
127        typedef std::map<std::string,handler_function> handler_map;
128        typedef std::map<std::string,std::string> alias_map;
129
130        handler_map get_handlers() {
131                handler_map handlers;
132                handlers["settings"] = boost::bind(&cli_parser::parse_settings, this, _1, _2);
133                handlers["service"] = boost::bind(&cli_parser::parse_service, this, _1, _2);
134                handlers["client"] = boost::bind(&cli_parser::parse_client, this, _1, _2, _T(""));
135                handlers["test"] = boost::bind(&cli_parser::parse_test, this, _1, _2);
136                handlers["help"] = boost::bind(&cli_parser::parse_help, this, _1, _2);
137                handlers["unit"] = boost::bind(&cli_parser::parse_unittest, this, _1, _2);
138                return handlers;
139        }
140
141        alias_map get_aliases() {
142                alias_map aliases;
143                aliases["nrpe"] = "NRPEClient";
144                aliases["nscp"] = "NSCPClient";
145                aliases["nsca"] = "NSCAClient";
146                aliases["eventlog"] = "CheckEventLog";
147                aliases["python"] = "PythonScript";
148                aliases["py"] = "PythonScript";
149                aliases["lua"] = "LuaScript";
150                aliases["syslog"] = "SyslogClient";
151                aliases["sys"] = "CheckSystem";
152                aliases["wmi"] = "CheckWMI";
153                return aliases;
154        }
155
156        int parse(int argc, wchar_t* argv[]) {
157
158                if (argc > 1 && argv[1][0] != L'-') {
159                        handler_map handlers = get_handlers();
160                        alias_map aliases = get_aliases();
161
162                        std::string mod = utf8::cvt<std::string>(argv[1]);
163                        handler_map::const_iterator it = handlers.find(mod);
164                        if (it != handlers.end())
165                                return it->second(argc-1, &argv[1]);
166
167                        alias_map::const_iterator alias_it = aliases.find(mod);
168                        if (alias_it != aliases.end())
169                                return parse_client(argc-1, &argv[1], utf8::cvt<std::wstring>(alias_it->second));
170
171                        parse_help(argc, argv);
172                        std::cerr << "Invalid module specified: " << mod << std::endl;
173                        return 1;
174                }
175                return parse_help(argc, argv);
176        }
177        int parse_help(int argc, wchar_t* argv[]) {
178                try {
179
180                        po::options_description all("Allowed options");
181                        all.add(root).add(common).add(service).add(settings).add(client).add(test).add(unittest);
182                        std::cout << all << std::endl;
183
184                        std::cerr << "First argument has to be one of the following: ";
185                        handler_map handlers = get_handlers();
186                        BOOST_FOREACH(const handler_map::value_type &itm, handlers) {
187                                std::cerr << itm.first << ", ";
188                        }
189                        std::cerr << std::endl;
190                        std::cerr << "Or on of the following client aliases: ";
191                        alias_map aliases = get_aliases();
192                        BOOST_FOREACH(const alias_map::value_type &itm, aliases) {
193                                std::cerr << itm.first << ", ";
194                        }
195                        std::cerr << std::endl;
196                        return 1;
197                } catch(std::exception & e) {
198                        std::cerr << "Unable to parse root option: " << e.what() << std::endl;
199                        return 1;
200                } catch (...) {
201                        std::cerr << "Unable to parse root option" << std::endl;
202                        return 1;
203                }
204        }
205
206        int parse_test(int argc, wchar_t* argv[]) {
207                try {
208
209                        po::options_description all("Allowed options (test)");
210                        all.add(common).add(test);
211
212                        po::variables_map vm;
213                        po::store(po::parse_command_line(argc, argv, all), vm);
214                        po::notify(vm);
215
216                        if (log_level.empty())
217                                log_level  = _T("debug");
218
219                        if (process_common_options("test", all))
220                                return 1;
221
222                        if (vm.count("log-to-file") == 0) {
223                                nsclient::logging::logger::set_backend("console");
224                        }
225
226                        nsclient::simple_client client(core_);
227                        client.start(log_level);
228                        return 0;
229                } catch(const std::exception & e) {
230                        std::cerr << std::string("Unable to parse command line (test): ") << e.what() << "\n";
231                        return 1;
232                }
233        }
234
235        int parse_settings(int argc, wchar_t* argv[]) {
236                try {
237                        po::options_description all("Allowed options (settings)");
238                        all.add(common).add(settings);
239
240                        po::variables_map vm;
241                        po::store(po::parse_command_line(argc, argv, all), vm);
242                        po::notify(vm);
243
244                        if (process_common_options("settings", all))
245                                return 1;
246
247                        bool def = vm.count("add-defaults")==1;
248                        bool load_all = vm.count("load-all")==1;
249
250                        nsclient::settings_client client(core_, log_level, def, load_all);
251                        int ret = -1;
252
253                        if (vm.count("generate")) {
254                                ret = client.generate(vm["generate"].as<std::wstring>());
255                        } else if (vm.count("migrate-to")) {
256                                ret = client.migrate_to(vm["migrate-to"].as<std::wstring>());
257                        } else if (vm.count("migrate-from")) {
258                                ret = client.migrate_from(vm["migrate-from"].as<std::wstring>());
259                        } else if (vm.count("set")) {
260                                ret = client.set(vm["path"].as<std::wstring>(), vm["key"].as<std::wstring>(), vm["set"].as<std::wstring>());
261                        } else if (vm.count("list")) {
262                                ret = client.list(vm["path"].as<std::wstring>());
263                        } else if (vm.count("show")) {
264                                ret = client.show(vm["path"].as<std::wstring>(), vm["key"].as<std::wstring>());
265                        } else if (vm.count("switch")) {
266                                client.switch_context(vm["switch"].as<std::wstring>());
267                                ret = 0;
268                        } else {
269                                std::cout << all << std::endl;
270                                return 1;
271                        }
272
273                        return ret;
274                } catch(const std::exception & e) {
275                        std::cerr << std::string("Unable to parse command line (settings): ") << e.what() << "\n";
276                        return 1;
277                }
278        }
279
280
281        int parse_service(int argc, wchar_t* argv[]) {
282                try {
283                        po::options_description all("Allowed options (service)");
284                        all.add(common).add(service);
285
286                        po::variables_map vm;
287                        po::store(po::parse_command_line(argc, argv, all), vm);
288                        po::notify(vm);
289
290                        if (process_common_options("service", all))
291                                return 1;
292
293                        std::wstring name;
294                        if (vm.count("name")) {
295                                name = vm["name"].as<std::wstring>();
296                        } else {
297                                get_logger()->info(__FILE__, __LINE__, _T("TODO retrieve name from service here"));
298                        }
299                        std::wstring desc;
300                        if (vm.count("description")) {
301                                desc = vm["description"].as<std::wstring>();
302                        } else {
303                                get_logger()->info(__FILE__, __LINE__, _T("TODO retrieve name from service here"));
304                        }
305                        if (nsclient::logging::logger::get_logger()->should_log(NSCAPI::log_level::debug)) {
306                                get_logger()->debug(__FILE__, __LINE__, _T("Service name: ") + name);
307                                get_logger()->debug(__FILE__, __LINE__, _T("Service description: ") + desc);
308                        }
309
310                        if (vm.count("run")) {
311                                try {
312                                        mainClient.start_and_wait(name);
313                                } catch (...) {
314                                        get_logger()->error(__FILE__, __LINE__, _T("Unknown exception in service"));
315                                }
316                        } else {
317                                nsclient::client::service_manager service_manager(name);
318
319                                if (vm.count("install")) {
320                                        service_manager.install(desc);
321                                } else if (vm.count("uninstall")) {
322                                        service_manager.uninstall();
323                                } else if (vm.count("start")) {
324                                        service_manager.start();
325                                } else if (vm.count("stop")) {
326                                        service_manager.stop();
327                                } else if (vm.count("info")) {
328                                        service_manager.info();
329                                } else {
330                                        std::cerr << "Missing argument" << std::endl;
331                                        return 1;
332                                }
333                        }
334                        return 0;
335                } catch(std::exception & e) {
336                        std::cerr << std::string("Unable to parse command line (settings): ") << e.what() << "\n";
337                        return 1;
338                }
339        }
340
341        struct client_arguments {
342                std::wstring command, combined_query, module;
343                std::vector<std::wstring> arguments;
344                enum modes { exec, query, submit, none, combined};
345                modes mode;
346                bool boot;
347                bool load_all;
348                client_arguments() : mode(none), boot(false), load_all(false) {}
349
350                void debug() {
351                        if (nsclient::logging::logger::get_logger()->should_log(NSCAPI::log_level::debug)) {
352                                get_logger()->info(__FILE__, __LINE__, _T("Module: ") + module);
353                                get_logger()->info(__FILE__, __LINE__, _T("Command: ") + command);
354                                get_logger()->info(__FILE__, __LINE__, _T("Extra Query: ") + combined_query);
355                                get_logger()->info(__FILE__, __LINE__, _T("Mode: ") + strEx::itos(mode));
356                                get_logger()->info(__FILE__, __LINE__, _T("Boot: ") + strEx::itos(boot));
357                                if (!module.empty() && boot)
358                                        get_logger()->info(__FILE__, __LINE__, _T("Warning module and boot specified only THAT module will be loaded"));
359                                std::wstring args;
360                                BOOST_FOREACH(std::wstring s, arguments)
361                                        strEx::append_list(args, s, _T(", "));
362                                get_logger()->info(__FILE__, __LINE__, _T("Arguments: ") + args);
363                        }
364
365                }
366        };
367        int parse_client(int argc, wchar_t* argv[], std::wstring module_ = _T("")) {
368                try {
369                        client_arguments args;
370
371                        args.module = module_;
372                        po::options_description all("Allowed options (client)");
373                        all.add(common).add(client);
374
375                        po::positional_options_description p;
376                        p.add("arguments", -1);
377
378                        po::variables_map vm;
379                        po::wparsed_options parsed =
380                                po::wcommand_line_parser(argc, argv).options(all).allow_unregistered().run();
381                        po::store(parsed, vm);
382                        po::notify(vm);
383
384                        if (process_common_options("client", all))
385                                return 1;
386
387
388                        if (vm.count("exec")) {
389                                args.command = vm["exec"].as<std::wstring>();
390                                args.mode = client_arguments::exec;
391                                if (vm.count("query")) {
392                                        args.combined_query = vm["query"].as<std::wstring>();
393                                        args.mode = client_arguments::combined;
394                                }
395                        } else if (vm.count("query")) {
396                                args.command = vm["query"].as<std::wstring>();
397                                args.mode = client_arguments::query;
398                        } else if (vm.count("submit")) {
399                                args.command = vm["submit"].as<std::wstring>();
400                                args.mode = client_arguments::submit;
401                        }
402                        args.load_all = vm.count("load-all")==1;
403
404                        if (vm.count("module"))
405                                args.module = vm["module"].as<std::wstring>();
406
407                        if (vm.count("boot"))
408                                args.boot = true;
409
410                        std::vector<std::wstring> kvp_args;
411                        if (vm.count("argument"))
412                                kvp_args = vm["argument"].as<std::vector<std::wstring> >();
413
414                        args.arguments = po::collect_unrecognized(parsed.options, po::include_positional);
415
416                        BOOST_FOREACH(std::wstring s, kvp_args) {
417                                std::wstring::size_type pos = s.find(L'=');
418                                if (pos == std::wstring::npos)
419                                        args.arguments.push_back(_T("--") + s);
420                                else {
421                                        args.arguments.push_back(_T("--") + s.substr(0,pos));
422                                        args.arguments.push_back(s.substr(pos+1));
423                                }
424                        }
425
426                        if (vm.count("raw-argument"))
427                                kvp_args = vm["raw-argument"].as<std::vector<std::wstring> >();
428                        BOOST_FOREACH(std::wstring s, kvp_args) {
429                                std::wstring::size_type pos = s.find(L'=');
430                                if (pos == std::wstring::npos)
431                                        args.arguments.push_back(s);
432                                else {
433                                        args.arguments.push_back(s.substr(0,pos));
434                                        args.arguments.push_back(s.substr(pos+1));
435                                }
436                        }
437                        return exec_client_mode(args);
438                } catch(const std::exception & e) {
439                        std::wcerr << _T("Client: Unable to parse command line: ") << utf8::to_unicode(e.what()) << std::endl;
440                        return 1;
441                } catch(...) {
442                        std::wcerr << _T("Client: Unable to parse command line: UNKNOWN") << std::endl;
443                        return 1;
444                }
445        }
446
447        int parse_unittest(int argc, wchar_t* argv[]) {
448                try {
449                        client_arguments args;
450                        settings_store = _T("dummy");
451                        po::options_description all("Allowed options (client)");
452                        all.add(common).add(unittest);
453
454                        po::positional_options_description p;
455                        p.add("arguments", -1);
456
457                        po::variables_map vm;
458                        po::wparsed_options parsed =
459                                po::wcommand_line_parser(argc, argv).options(all).allow_unregistered().run();
460                        po::store(parsed, vm);
461                        po::notify(vm);
462
463                        if (process_common_options("unitest", all))
464                                return 1;
465
466
467                        if (vm.count("language")) {
468                                std::wstring lang = vm["language"].as<std::wstring>();
469                                if (lang == _T("python") || lang == _T("py")) {
470                                        args.command = _T("python-script");
471                                        args.combined_query = _T("py_unittest");
472                                        args.mode = client_arguments::combined;
473                                        args.module = _T("PythonScript");
474                                } else if (lang == _T("lua")) {
475                                                args.command = _T("LUAScript.run");
476                                                args.combined_query = _T("lua_unittest");
477                                                args.mode = client_arguments::combined;
478                                                args.module = _T("LuaScript");
479                                } else {
480                                        std::wcerr << _T("Unknown language: ") << lang << std::endl;
481                                        return 1;
482                                }
483                        } else {
484                                args.command = _T("python-script");
485                                args.combined_query = _T("py_unittest");
486                                args.mode = client_arguments::combined;
487                                args.module = _T("PythonScript");
488                        }
489
490                        std::vector<std::wstring> kvp_args;
491                        if (vm.count("argument"))
492                                kvp_args = vm["argument"].as<std::vector<std::wstring> >();
493
494                        args.arguments = po::collect_unrecognized(parsed.options, po::include_positional);
495
496                        BOOST_FOREACH(std::wstring s, kvp_args) {
497                                std::wstring::size_type pos = s.find(L'=');
498                                if (pos == std::wstring::npos)
499                                        args.arguments.push_back(_T("--") + s);
500                                else {
501                                        args.arguments.push_back(_T("--") + s.substr(0,pos));
502                                        args.arguments.push_back(s.substr(pos+1));
503                                }
504                        }
505
506                        if (vm.count("raw-argument"))
507                                kvp_args = vm["raw-argument"].as<std::vector<std::wstring> >();
508                        BOOST_FOREACH(std::wstring s, kvp_args) {
509                                std::wstring::size_type pos = s.find(L'=');
510                                if (pos == std::wstring::npos)
511                                        args.arguments.push_back(s);
512                                else {
513                                        args.arguments.push_back(s.substr(0,pos));
514                                        args.arguments.push_back(s.substr(pos+1));
515                                }
516                        }
517                        return exec_client_mode(args);
518                } catch(const std::exception & e) {
519                        std::wcerr << _T("Client: Unable to parse command line: ") << utf8::to_unicode(e.what()) << std::endl;
520                        return 1;
521                } catch(...) {
522                        std::wcerr << _T("Client: Unable to parse command line: UNKNOWN") << std::endl;
523                        return 1;
524                }
525        }
526
527        int exec_client_mode(client_arguments &args) {
528                try {
529                        args.debug();
530
531                        core_->boot_init(log_level);
532                        if (args.load_all)                                                                                                                                                   
533                                core_->preboot_load_all_plugin_files();
534                        if (args.module.empty())
535                                core_->boot_load_all_plugins();
536                        else
537                                core_->boot_load_plugin(args.module);
538                        core_->boot_start_plugins(args.boot);
539                        int ret = 0;
540                        std::list<std::wstring> resp;
541                        if (args.mode == client_arguments::none) {
542                                args.mode = client_arguments::exec;
543                                std::wcerr << _T("Since no mode was specified assuming --exec (other options are --query and --submit)") << std::endl;
544                        }
545                        if (args.mode == client_arguments::query) {
546                                ret = mainClient.simple_query(args.module, args.command, args.arguments, resp);
547                        } else if (args.mode == client_arguments::exec || args.mode == client_arguments::combined) {
548                                ret = mainClient.simple_exec(args.command, args.arguments, resp);
549                                if (ret == NSCAPI::returnIgnored) {
550                                        ret = 1;
551                                        std::wcout << _T("Command not found (by module): ") << args.command << std::endl;
552                                        resp.push_back(_T("Command not found: ") + args.command);
553                                        mainClient.simple_exec(_T("help"), args.arguments, resp);
554                                } else if (args.mode == client_arguments::combined) {
555                                        if (ret == NSCAPI::returnOK) {
556                                                mainClient.reload(_T("service"));
557                                                ret = mainClient.simple_query(args.module, args.combined_query, args.arguments, resp);
558                                        } else {
559                                                std::wcerr << _T("Failed to execute command, will not attempt query") << std::endl;
560                                        }
561                                }
562                        } else if (args.mode == client_arguments::submit) {
563                                std::wcerr << _T("--submit is currently not supported (but you can use --exec submit which is technically the same)") << std::endl;
564                        } else {
565                                std::wcerr << _T("Need to specify one of --exec, --query or --submit") << std::endl;
566                        }
567                        mainClient.stop_unload_plugins_pre();
568                        mainClient.stop_exit_pre();
569                        mainClient.stop_exit_post();
570
571                        BOOST_FOREACH(std::wstring r, resp) {
572                                std::wcout << r << std::endl;
573                        }
574                        return ret;
575                } catch(const std::exception & e) {
576                        std::wcerr << _T("Client: Unable to parse command line: ") << utf8::to_unicode(e.what()) << std::endl;
577                        return 1;
578                } catch(...) {
579                        std::wcerr << _T("Client: Unable to parse command line: UNKNOWN") << std::endl;
580                        return 1;
581                }
582        }
583};
584
585
586
587
588
Note: See TracBrowser for help on using the repository browser.