source: nscp/modules/NRPEClient/NRPEClient.cpp @ 96c1461

0.4.00.4.10.4.2
Last change on this file since 96c1461 was 96c1461, checked in by Michael Medin <michael@…>, 18 months ago
  • Major refactoring in the command line interface
  • Added support for alias to many common module (command line) so: nscp eventlog (is the same as nscp client --module CheckEventLog)
  • Fixed issue with CheckEventLog message rendering and eventid
  • Refactored all Client modules to all support command line, commands and submissions.
  • Added uniform handling of "everything" to all Client plugins
  • Fixed SyslogClient to work "as advertised" (ie. all hardcoded stuff is removed)
  • Fixed utf8 issue with text strings (now have a working concept which needs to be implementd "all over the place")
  • Many issues and fixes related to clients.
  • Fixed so CheckEvent? log (insert) works much better (added new options)
  • Property mode set to 100644
File size: 17.6 KB
Line 
1/**************************************************************************
2*   Copyright (C) 2004-2007 by Michael Medin <michael@medin.name>         *
3*                                                                         *
4*   This code is part of NSClient++ - http://trac.nakednuns.org/nscp      *
5*                                                                         *
6*   This program is free software; you can redistribute it and/or modify  *
7*   it under the terms of the GNU General Public License as published by  *
8*   the Free Software Foundation; either version 2 of the License, or     *
9*   (at your option) any later version.                                   *
10*                                                                         *
11*   This program is distributed in the hope that it will be useful,       *
12*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14*   GNU General Public License for more details.                          *
15*                                                                         *
16*   You should have received a copy of the GNU General Public License     *
17*   along with this program; if not, write to the                         *
18*   Free Software Foundation, Inc.,                                       *
19*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
20***************************************************************************/
21#include "stdafx.h"
22#include "NRPEClient.h"
23
24#include <time.h>
25#include <strEx.h>
26
27#include <strEx.h>
28#include <nrpe/client/socket.hpp>
29
30#include <settings/client/settings_client.hpp>
31
32namespace sh = nscapi::settings_helper;
33
34/**
35 * Default c-tor
36 * @return
37 */
38NRPEClient::NRPEClient() {}
39
40/**
41 * Default d-tor
42 * @return
43 */
44NRPEClient::~NRPEClient() {}
45
46/**
47 * Load (initiate) module.
48 * Start the background collector thread and let it run until unloadModule() is called.
49 * @return true
50 */
51bool NRPEClient::loadModule() {
52        return false;
53}
54
55bool NRPEClient::loadModuleEx(std::wstring alias, NSCAPI::moduleLoadMode mode) {
56        std::map<std::wstring,std::wstring> commands;
57
58        std::wstring certificate;
59        unsigned int timeout = 30, buffer_length = 1024;
60        bool use_ssl = true;
61        try {
62
63                sh::settings_registry settings(get_settings_proxy());
64                settings.set_alias(_T("NRPE"), alias, _T("client"));
65
66                target_path = settings.alias().get_settings_path(_T("targets"));
67
68                settings.alias().add_path_to_settings()
69                        (_T("NRPE CLIENT SECTION"), _T("Section for NRPE active/passive check module."))
70
71                        (_T("handlers"), sh::fun_values_path(boost::bind(&NRPEClient::add_command, this, _1, _2)),
72                        _T("CLIENT HANDLER SECTION"), _T(""))
73
74                        (_T("targets"), sh::fun_values_path(boost::bind(&NRPEClient::add_target, this, _1, _2)),
75                        _T("REMOTE TARGET DEFINITIONS"), _T(""))
76                        ;
77
78                settings.alias().add_key_to_settings()
79                        (_T("channel"), sh::wstring_key(&channel_, _T("NRPE")),
80                        _T("CHANNEL"), _T("The channel to listen to."))
81
82                        ;
83
84                settings.alias(_T("/targets/default")).add_key_to_settings()
85
86                        (_T("timeout"), sh::uint_key(&timeout, 30),
87                        _T("TIMEOUT"), _T("Timeout when reading/writing packets to/from sockets."))
88
89                        (_T("use ssl"), sh::bool_key(&use_ssl, true),
90                        _T("ENABLE SSL ENCRYPTION"), _T("This option controls if SSL should be enabled."))
91
92                        (_T("certificate"), sh::wpath_key(&certificate, _T("${certificate-path}/nrpe_dh_512.pem")),
93                        _T("SSL CERTIFICATE"), _T(""))
94
95                        (_T("payload length"),  sh::uint_key(&buffer_length, 1024),
96                        _T("PAYLOAD LENGTH"), _T("Length of payload to/from the NRPE agent. This is a hard specific value so you have to \"configure\" (read recompile) your NRPE agent to use the same value for it to work."))
97                        ;
98
99                settings.register_all();
100                settings.notify();
101
102                get_core()->registerSubmissionListener(get_id(), channel_);
103
104                if (!targets.has_target(_T("default"))) {
105                        add_target(_T("default"), _T("default"));
106                        targets.rebuild();
107                }
108                nscapi::target_handler::optarget t = targets.find_target(_T("default"));
109                if (t) {
110                        if (!t->has_option("certificate"))
111                                t->options[_T("certificate")] = certificate;
112                        if (!t->has_option("timeout"))
113                                t->options[_T("timeout")] = strEx::itos(timeout);
114                        if (!t->has_option("payload length"))
115                                t->options[_T("payload length")] = strEx::itos(buffer_length);
116                        if (!t->has_option("ssl"))
117                                t->options[_T("ssl")] = use_ssl?_T("true"):_T("false");
118                        targets.add(*t);
119                } else {
120                        NSC_LOG_ERROR(_T("Default target not found!"));
121                }
122
123        } catch (nscapi::nscapi_exception &e) {
124                NSC_LOG_ERROR_STD(_T("NSClient API exception: ") + utf8::to_unicode(e.what()));
125                return false;
126        } catch (std::exception &e) {
127                NSC_LOG_ERROR_STD(_T("Exception caught: ") + utf8::to_unicode(e.what()));
128                return false;
129        } catch (...) {
130                NSC_LOG_ERROR_STD(_T("Exception caught: <UNKNOWN EXCEPTION>"));
131                return false;
132        }
133        return true;
134}
135std::string get_command(std::string alias, std::string command = "") {
136        if (!alias.empty())
137                return alias;
138        if (!command.empty())
139                return command;
140        return "_NRPE_CHECK";
141}
142
143//////////////////////////////////////////////////////////////////////////
144// Settings helpers
145//
146
147void NRPEClient::add_target(std::wstring key, std::wstring arg) {
148        try {
149                nscapi::target_handler::target t = targets.add(get_settings_proxy(), target_path , key, arg);
150                if (t.has_option(_T("certificate"))) {
151                        boost::filesystem::wpath p = t.options[_T("certificate")];
152                        if (!boost::filesystem::is_regular(p)) {
153                                p = get_core()->getBasePath() / p;
154                                t.options[_T("certificate")] = utf8::cvt<std::wstring>(p.string());
155                                targets.add(t);
156                        }
157                        if (boost::filesystem::is_regular(p)) {
158                                NSC_DEBUG_MSG_STD(_T("Using certificate: ") + p.string());
159                        } else {
160                                NSC_LOG_ERROR_STD(_T("Certificate not found: ") + p.string());
161                        }
162                }
163        } catch (...) {
164                NSC_LOG_ERROR_STD(_T("Failed to add target: ") + key);
165        }
166}
167
168void NRPEClient::add_command(std::wstring name, std::wstring args) {
169        try {
170                std::wstring key = commands.add_command(name, args);
171                if (!key.empty())
172                        register_command(key.c_str(), _T("NRPE relay for: ") + name);
173        } catch (boost::program_options::validation_error &e) {
174                NSC_LOG_ERROR_STD(_T("Could not add command ") + name + _T(": ") + utf8::to_unicode(e.what()));
175        } catch (...) {
176                NSC_LOG_ERROR_STD(_T("Could not add command ") + name);
177        }
178}
179
180/**
181 * Unload (terminate) module.
182 * Attempt to stop the background processing thread.
183 * @return true if successfully, false if not (if not things might be bad)
184 */
185bool NRPEClient::unloadModule() {
186        return true;
187}
188
189NSCAPI::nagiosReturn NRPEClient::handleCommand(const std::wstring &target, const std::wstring &command, std::list<std::wstring> &arguments, std::wstring &message, std::wstring &perf) {
190        std::wstring cmd = client::command_line_parser::parse_command(command, _T("nrpe"));
191
192        client::configuration config;
193        setup(config);
194        if (cmd == _T("query"))
195                return client::command_line_parser::query(config, cmd, arguments, message, perf);
196        if (cmd == _T("submit")) {
197                boost::tuple<int,std::wstring> result = client::command_line_parser::simple_submit(config, cmd, arguments);
198                message = result.get<1>();
199                return result.get<0>();
200        }
201        if (cmd == _T("exec")) {
202                return client::command_line_parser::exec(config, cmd, arguments, message);
203        }
204        return commands.exec_simple(config, target, command, arguments, message, perf);
205}
206
207int NRPEClient::commandLineExec(const std::wstring &command, std::list<std::wstring> &arguments, std::wstring &result) {
208        std::wstring cmd = client::command_line_parser::parse_command(command, _T("nrpe"));
209        if (!client::command_line_parser::is_command(cmd))
210                return NSCAPI::returnIgnored;
211
212        client::configuration config;
213        setup(config);
214        return client::command_line_parser::commandLineExec(config, cmd, arguments, result);
215}
216
217NSCAPI::nagiosReturn NRPEClient::handleRAWNotification(const wchar_t* channel, std::string request, std::string &response) {
218        try {
219                client::configuration config;
220                setup(config);
221
222                if (!client::command_line_parser::relay_submit(config, request, response)) {
223                        NSC_LOG_ERROR_STD(_T("Failed to submit message..."));
224                        return NSCAPI::hasFailed;
225                }
226                return NSCAPI::isSuccess;
227        } catch (std::exception &e) {
228                NSC_LOG_ERROR_STD(_T("Failed to send data: ") + utf8::to_unicode(e.what()));
229                return NSCAPI::hasFailed;
230        } catch (...) {
231                NSC_LOG_ERROR_STD(_T("Failed to send data: UNKNOWN"));
232                return NSCAPI::hasFailed;
233        }
234}
235
236//////////////////////////////////////////////////////////////////////////
237// Parser setup/Helpers
238//
239
240void NRPEClient::add_local_options(po::options_description &desc, client::configuration::data_type data) {
241        desc.add_options()
242                ("certificate,c", po::value<std::string>()->notifier(boost::bind(&nscapi::functions::destination_container::set_string_data, &data->recipient, "certificate", _1)),
243                        "Length of payload (has to be same as on the server)")
244
245                ("buffer-length,l", po::value<unsigned int>()->notifier(boost::bind(&nscapi::functions::destination_container::set_int_data, &data->recipient, "payload length", _1)),
246                        "Length of payload (has to be same as on the server)")
247
248                ("no-ssl,n", po::value<bool>()->zero_tokens()->default_value(false)->notifier(boost::bind(&nscapi::functions::destination_container::set_bool_data, &data->recipient, "no ssl", _1)),
249                        "Do not initial an ssl handshake with the server, talk in plaintext.")
250                ;
251}
252
253void NRPEClient::setup(client::configuration &config) {
254        boost::shared_ptr<clp_handler_impl> handler = boost::shared_ptr<clp_handler_impl>(new clp_handler_impl(this));
255        add_local_options(config.local, config.data);
256
257        net::wurl url;
258        url.protocol = _T("nrpe");
259        url.port = 5666;
260        nscapi::target_handler::optarget opt = targets.find_target(_T("default"));
261        if (opt) {
262                nscapi::target_handler::target t = *opt;
263                url.host = t.host;
264                if (t.has_option("port")) {
265                        try {
266                                url.port = strEx::stoi(t.options[_T("port")]);
267                        } catch (...) {}
268                }
269                std::string keys[] = {"certificate", "timeout", "payload length", "ssl"};
270                BOOST_FOREACH(std::string s, keys) {
271                        config.data->recipient.data[s] = utf8::cvt<std::string>(t.options[utf8::cvt<std::wstring>(s)]);
272                }
273        }
274        config.data->recipient.id = "default";
275        config.data->recipient.address = utf8::cvt<std::string>(url.to_string());
276        config.data->host_self.id = "self";
277        //config.data->host_self.host = hostname_;
278
279        config.target_lookup = handler;
280        config.handler = handler;
281}
282
283NRPEClient::connection_data NRPEClient::parse_header(const ::Plugin::Common_Header &header) {
284        nscapi::functions::destination_container recipient;
285        nscapi::functions::parse_destination(header, header.recipient_id(), recipient, true);
286        return connection_data(recipient);
287}
288
289//////////////////////////////////////////////////////////////////////////
290// Parser implementations
291//
292
293int NRPEClient::clp_handler_impl::query(client::configuration::data_type data, ::Plugin::Common_Header* header, const std::string &request, std::string &reply) {
294        NSCAPI::nagiosReturn ret = NSCAPI::returnOK;
295        try {
296                Plugin::QueryRequestMessage request_message;
297                request_message.ParseFromString(request);
298                connection_data con = parse_header(*header);
299
300                Plugin::QueryResponseMessage response_message;
301                nscapi::functions::create_simple_header(response_message.mutable_header());     // TODO copy request header (inverted)
302
303                for (int i=0;i<request_message.payload_size();i++) {
304                        std::string command = get_command(request_message.payload(i).alias(), request_message.payload(i).command());
305                        std::string data = command;
306                        for (int a=0;a<request_message.payload(i).arguments_size();a++) {
307                                data += "!" + request_message.payload(i).arguments(a);
308                        }
309                        boost::tuple<int,std::wstring> ret = instance->send(con, data);
310                        std::pair<std::wstring,std::wstring> rdata = strEx::split(ret.get<1>(), _T("|"));
311                        nscapi::functions::append_simple_query_response_payload(response_message.add_payload(), utf8::cvt<std::wstring>(command), ret.get<0>(), rdata.first, rdata.second);
312                }
313                response_message.SerializeToString(&reply);
314                return NSCAPI::isSuccess;
315        } catch (std::exception &e) {
316                NSC_LOG_ERROR_STD(_T("Exception: ") + utf8::to_unicode(e.what()));
317                nscapi::functions::create_simple_query_response(_T("command"), NSCAPI::returnUNKNOWN, _T("Exception: ") + utf8::to_unicode(e.what()), _T(""), reply);
318                return NSCAPI::returnUNKNOWN;
319        }
320}
321
322int NRPEClient::clp_handler_impl::submit(client::configuration::data_type data, ::Plugin::Common_Header* header, const std::string &request, std::string &reply) {
323        std::wstring channel;
324        try {
325                Plugin::SubmitRequestMessage message;
326                message.ParseFromString(request);
327                connection_data con = parse_header(*header);
328                channel = utf8::cvt<std::wstring>(message.channel());
329               
330                for (int i=0;i<message.payload_size();++i) {
331                        std::string command = get_command(message.payload(i).alias(), message.payload(i).command());
332                        std::string data = command;
333                        for (int a=0;a<message.payload(i).arguments_size();a++) {
334                                data += "!" + message.payload(i).arguments(i);
335                        }
336                        boost::tuple<int,std::wstring> ret = instance->send(con, data);
337                        // TODO: Change this to append!
338                        nscapi::functions::create_simple_submit_response(channel, utf8::cvt<std::wstring>(command), ret.get<0>(), _T("Message submitted successfully: ") + ret.get<1>(), reply);
339                        return NSCAPI::isSuccess;
340                }
341                nscapi::functions::create_simple_submit_response(channel, _T("UNKNOWN"), NSCAPI::returnUNKNOWN, _T("Empty message was submitted"), reply);
342                return NSCAPI::isSuccess;
343        } catch (std::exception &e) {
344                NSC_LOG_ERROR_STD(_T("Exception: ") + utf8::to_unicode(e.what()));
345                nscapi::functions::create_simple_submit_response(channel, _T("UNKNOWN"), NSCAPI::returnUNKNOWN, utf8::to_unicode(e.what()), reply);
346                return NSCAPI::hasFailed;
347        }       
348}
349
350int NRPEClient::clp_handler_impl::exec(client::configuration::data_type data, ::Plugin::Common_Header* header, const std::string &request, std::string &reply) {
351        NSCAPI::nagiosReturn ret = NSCAPI::returnOK;
352        try {
353                Plugin::ExecuteRequestMessage request_message;
354                request_message.ParseFromString(request);
355                connection_data con = parse_header(*header);
356
357                for (int i=0;i<request_message.payload_size();i++) {
358                        std::string command = get_command(request_message.payload(i).command());
359                        std::string data = command;
360                        for (int a=0;a<request_message.payload(i).arguments_size();a++) {
361                                data += "!" + request_message.payload(i).arguments(a);
362                        }
363                        boost::tuple<int,std::wstring> ret = instance->send(con, data);
364                        nscapi::functions::create_simple_exec_response(utf8::cvt<std::wstring>(command), ret.get<0>(), ret.get<1>(), reply);
365                }
366                return NSCAPI::isSuccess;
367        } catch (std::exception &e) {
368                NSC_LOG_ERROR_STD(_T("Exception: ") + utf8::to_unicode(e.what()));
369                nscapi::functions::create_simple_exec_response(_T("command"), NSCAPI::returnUNKNOWN, _T("Exception: ") + utf8::to_unicode(e.what()), reply);
370                return NSCAPI::hasFailed;
371        }
372}
373
374//////////////////////////////////////////////////////////////////////////
375// Protocol implementations
376//
377
378boost::tuple<int,std::wstring> NRPEClient::send(connection_data con, const std::string data) {
379        try {
380                NSC_DEBUG_MSG_STD(_T("NRPE Connection details: ") + con.to_wstring());
381                NSC_DEBUG_MSG_STD(_T("NRPE data: ") + utf8::cvt<std::wstring>(data));
382                nrpe::packet packet;
383                if (con.use_ssl) {
384#ifdef USE_SSL
385                        packet = send_ssl(con.cert, con.host, con.port, con.timeout, nrpe::packet::make_request(utf8::cvt<std::wstring>(data), con.buffer_length));
386#else
387                        NSC_LOG_ERROR_STD(_T("SSL not avalible (compiled without USE_SSL)"));
388                        return boost::tie(NSCAPI::returnUNKNOWN, _T("SSL support not available (compiled without USE_SSL)"));
389#endif
390                } else
391                        packet = send_nossl(con.host, con.port, con.timeout, nrpe::packet::make_request(utf8::cvt<std::wstring>(data), con.buffer_length));
392                return boost::make_tuple(static_cast<int>(packet.getResult()), packet.getPayload());
393        } catch (nrpe::nrpe_packet_exception &e) {
394                return boost::tie(NSCAPI::returnUNKNOWN, _T("NRPE Packet errro: ") + e.wwhat());
395        } catch (std::runtime_error &e) {
396                NSC_LOG_ERROR_STD(_T("Socket error: ") + utf8::to_unicode(e.what()));
397                return boost::tie(NSCAPI::returnUNKNOWN, _T("Socket error: ") + utf8::to_unicode(e.what()));
398        } catch (std::exception &e) {
399                NSC_LOG_ERROR_STD(_T("Error: ") + utf8::to_unicode(e.what()));
400                return boost::tie(NSCAPI::returnUNKNOWN, _T("Error: ") + utf8::to_unicode(e.what()));
401        } catch (...) {
402                return boost::tie(NSCAPI::returnUNKNOWN, _T("Unknown error -- REPORT THIS!"));
403        }
404}
405
406
407#ifdef USE_SSL
408nrpe::packet NRPEClient::send_ssl(std::string cert, std::string host, std::string port, int timeout, nrpe::packet packet) {
409        boost::asio::io_service io_service;
410        boost::asio::ssl::context ctx(io_service, boost::asio::ssl::context::sslv23);
411        SSL_CTX_set_cipher_list(ctx.impl(), "ADH");
412        ctx.use_tmp_dh_file(to_string(cert));
413        ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
414        nrpe::client::ssl_socket socket(io_service, ctx, host, port);
415        socket.send(packet, boost::posix_time::seconds(timeout));
416        return socket.recv(packet, boost::posix_time::seconds(timeout));
417}
418#endif
419
420nrpe::packet NRPEClient::send_nossl(std::string host, std::string port, int timeout, nrpe::packet packet) {
421        boost::asio::io_service io_service;
422        nrpe::client::socket socket(io_service, host, port);
423        socket.send(packet, boost::posix_time::seconds(timeout));
424        return socket.recv(packet, boost::posix_time::seconds(timeout));
425}
426
427NSC_WRAP_DLL();
428NSC_WRAPPERS_MAIN_DEF(NRPEClient);
429NSC_WRAPPERS_IGNORE_MSG_DEF();
430NSC_WRAPPERS_HANDLE_CMD_DEF();
431NSC_WRAPPERS_CLI_DEF();
432NSC_WRAPPERS_HANDLE_NOTIFICATION_DEF();
433
Note: See TracBrowser for help on using the repository browser.