source: nscp/modules/DistributedClient/DistributedClient.cpp @ 2906cda

0.4.00.4.10.4.2
Last change on this file since 2906cda was 2906cda, checked in by Michael Medin <michael@…>, 16 months ago
  • Fixed installer issue (could not start service)
  • Did some refactoring in the distributed client
  • Property mode set to 100644
File size: 15.4 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 "DistributedClient.h"
23#include <time.h>
24#include <boost/filesystem.hpp>
25
26#include <strEx.h>
27#include <net/net.hpp>
28#include <nscp/client/socket.hpp>
29#include <zeromq/client.hpp>
30
31#include <protobuf/plugin.pb.h>
32
33#include <settings/client/settings_client.hpp>
34
35namespace sh = nscapi::settings_helper;
36
37const std::wstring DistributedClient::command_prefix = _T("dnscp");
38/**
39* Default c-tor
40* @return
41*/
42DistributedClient::DistributedClient() {}
43
44/**
45* Default d-tor
46* @return
47*/
48DistributedClient::~DistributedClient() {}
49
50/**
51* Load (initiate) module.
52* Start the background collector thread and let it run until unloadModule() is called.
53* @return true
54*/
55bool DistributedClient::loadModule() {
56        return false;
57}
58
59bool DistributedClient::loadModuleEx(std::wstring alias, NSCAPI::moduleLoadMode mode) {
60        std::map<std::wstring,std::wstring> commands;
61
62        try {
63
64
65                sh::settings_registry settings(get_settings_proxy());
66                settings.set_alias(_T("distributed"), alias, _T("client"));
67                target_path = settings.alias().get_settings_path(_T("targets"));
68
69                settings.alias().add_path_to_settings()
70                        (_T("Distributed NSCP CLIENT SECTION"), _T("Section for NSCP active/passive check module."))
71
72                        (_T("handlers"), sh::fun_values_path(boost::bind(&DistributedClient::add_command, this, _1, _2)),
73                        _T("CLIENT HANDLER SECTION"), _T(""))
74
75                        (_T("targets"), sh::fun_values_path(boost::bind(&DistributedClient::add_target, this, _1, _2)),
76                        _T("REMOTE TARGET DEFINITIONS"), _T(""))
77
78                        ;
79
80                settings.alias().add_key_to_settings()
81                        (_T("channel"), sh::wstring_key(&channel_, _T("DNSCP")),
82                        _T("CHANNEL"), _T("The channel to listen to."))
83
84                        ;
85
86                settings.register_all();
87                settings.notify();
88
89                targets.add_missing(get_settings_proxy(), target_path, _T("default"), _T(""), true);
90
91
92                get_core()->registerSubmissionListener(get_id(), channel_);
93                register_command(_T("dnscp_query"), _T("Submit a query to a remote host via NSCP"));
94                register_command(_T("dnscp_forward"), _T("Forward query to remote NSCP host"));
95                register_command(_T("dnscp_submit"), _T("Submit a query to a remote host via NSCP"));
96                register_command(_T("dnscp_exec"), _T("Execute remote command on a remote host via NSCP"));
97                register_command(_T("dnscp_help"), _T("Help on using NSCP Client"));
98
99        } catch (nscapi::nscapi_exception &e) {
100                NSC_LOG_ERROR_STD(_T("NSClient API exception: ") + utf8::to_unicode(e.what()));
101                return false;
102        } catch (std::exception &e) {
103                NSC_LOG_ERROR_STD(_T("Exception caught: ") + utf8::to_unicode(e.what()));
104                return false;
105        } catch (...) {
106                NSC_LOG_ERROR_STD(_T("Exception caught: <UNKNOWN EXCEPTION>"));
107                return false;
108        }
109        return true;
110}
111std::string get_command(std::string alias, std::string command = "") {
112        if (!alias.empty())
113                return alias;
114        if (!command.empty())
115                return command;
116        return "_NRPE_CHECK";
117}
118
119//////////////////////////////////////////////////////////////////////////
120// Settings helpers
121//
122
123void DistributedClient::add_target(std::wstring key, std::wstring arg) {
124        try {
125                targets.add(get_settings_proxy(), target_path , key, arg);
126        } catch (const std::exception &e) {
127                NSC_LOG_ERROR_STD(_T("Failed to add target: ") + key + _T(", ") + utf8::to_unicode(e.what()));
128        } catch (...) {
129                NSC_LOG_ERROR_STD(_T("Failed to add target: ") + key);
130        }
131}
132
133void DistributedClient::add_command(std::wstring name, std::wstring args) {
134        try {
135                std::wstring key = commands.add_command(name, args);
136                if (!key.empty())
137                        register_command(key.c_str(), _T("DNSCP relay for: ") + name);
138        } catch (boost::program_options::validation_error &e) {
139                NSC_LOG_ERROR_STD(_T("Could not add command ") + name + _T(": ") + utf8::to_unicode(e.what()));
140        } catch (...) {
141                NSC_LOG_ERROR_STD(_T("Could not add command ") + name);
142        }
143}
144
145/**
146* Unload (terminate) module.
147* Attempt to stop the background processing thread.
148* @return true if successfully, false if not (if not things might be bad)
149*/
150bool DistributedClient::unloadModule() {
151        return true;
152}
153
154NSCAPI::nagiosReturn DistributedClient::handleRAWCommand(const wchar_t* char_command, const std::string &request, std::string &result) {
155        std::wstring cmd = client::command_line_parser::parse_command(char_command, command_prefix);
156
157        Plugin::QueryRequestMessage message;
158        message.ParseFromString(request);
159
160        client::configuration config;
161        setup(config, message.header());
162
163        return commands.process_query(cmd, config, message, result);
164}
165
166int DistributedClient::commandRAWLineExec(const wchar_t* char_command, const std::string &request, std::string &result) {
167        std::wstring cmd = client::command_line_parser::parse_command(char_command, command_prefix);
168
169        Plugin::ExecuteRequestMessage message;
170        message.ParseFromString(request);
171
172        client::configuration config;
173        setup(config, message.header());
174
175        return commands.process_exec(cmd, config, message, result);
176}
177
178NSCAPI::nagiosReturn DistributedClient::handleRAWNotification(const wchar_t* channel, std::string request, std::string &result) {
179        Plugin::SubmitRequestMessage message;
180        message.ParseFromString(request);
181
182        client::configuration config;
183        setup(config, message.header());
184
185        return client::command_line_parser::do_relay_submit(config, message, result);
186}
187
188//////////////////////////////////////////////////////////////////////////
189// Parser setup/Helpers
190//
191
192void DistributedClient::add_local_options(po::options_description &desc, client::configuration::data_type data) {
193        desc.add_options()
194                ("certificate,c", po::value<std::string>()->notifier(boost::bind(&nscapi::functions::destination_container::set_string_data, &data->recipient, "certificate", _1)),
195                "Length of payload (has to be same as on the server)")
196                /*
197                ("no-ssl,n", po::value<bool>(&command_data.no_ssl)->zero_tokens()->default_value(false), "Do not initial an ssl handshake with the server, talk in plain text.")
198
199                ("cert,c", po::value<std::wstring>(&command_data.cert)->default_value(cert_), "Certificate to use.")
200                */
201                ;
202}
203
204void DistributedClient::setup(client::configuration &config, const ::Plugin::Common_Header& header) {
205        boost::shared_ptr<clp_handler_impl> handler = boost::shared_ptr<clp_handler_impl>(new clp_handler_impl(this));
206        add_local_options(config.local, config.data);
207
208        config.data->recipient.id = header.recipient_id();
209        std::wstring recipient = utf8::cvt<std::wstring>(config.data->recipient.id);
210        if (!targets.has_object(recipient)) {
211                NSC_LOG_ERROR(_T("Target not found (using default): ") + recipient);
212                recipient = _T("default");
213        }
214        nscapi::targets::optional_target_object opt = targets.find_object(recipient);
215
216        if (opt) {
217                nscapi::targets::target_object t = *opt;
218                nscapi::functions::destination_container def = t.to_destination_container();
219                config.data->recipient.apply(def);
220        }
221        config.data->host_self.id = "self";
222        //config.data->host_self.host = hostname_;
223
224        config.target_lookup = handler;
225        config.handler = handler;
226}
227
228DistributedClient::connection_data DistributedClient::parse_header(const ::Plugin::Common_Header &header, client::configuration::data_type data) {
229        nscapi::functions::destination_container recipient;
230        nscapi::functions::parse_destination(header, header.recipient_id(), recipient, true);
231        return connection_data(recipient, data->recipient);
232}
233
234//////////////////////////////////////////////////////////////////////////
235// Parser implementations
236//
237
238std::string gather_and_log_errors(std::string  &payload) {
239        NSCPIPC::ErrorMessage message;
240        message.ParseFromString(payload);
241        std::string ret;
242        for (int i=0;i<message.error_size();i++) {
243                ret += message.error(i).message();
244                NSC_LOG_ERROR_STD(_T("Error: ") + utf8::cvt<std::wstring>(message.error(i).message()));
245        }
246        return ret;
247}
248int DistributedClient::clp_handler_impl::query(client::configuration::data_type data, const Plugin::QueryRequestMessage &request_message, std::string &reply) {
249        const ::Plugin::Common_Header& request_header = request_message.header();
250        int ret = NSCAPI::returnUNKNOWN;
251        connection_data con = parse_header(request_header, data);
252
253        Plugin::QueryResponseMessage response_message;
254        nscapi::functions::make_return_header(response_message.mutable_header(), request_header);
255
256        std::list<nscp::packet> chunks;
257        chunks.push_back(nscp::factory::create_envelope_request(1));
258        chunks.push_back(nscp::factory::create_payload(nscp::data::command_request, request_message.SerializeAsString(), 0));
259        chunks = instance->send(con, chunks);
260        BOOST_FOREACH(nscp::packet &chunk, chunks) {
261                if (nscp::checks::is_query_response(chunk)) {
262                        nscapi::functions::append_response_payloads(response_message, chunk.payload);
263                } else if (nscp::checks::is_error(chunk)) {
264                        std::string error = gather_and_log_errors(chunk.payload);
265                        nscapi::functions::append_simple_query_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, error);
266                        ret = NSCAPI::returnUNKNOWN;
267                } else {
268                        NSC_LOG_ERROR_STD(_T("Unsupported message type: ") + strEx::itos(chunk.signature.payload_type));
269                        nscapi::functions::append_simple_query_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, "Unsupported response type");
270                        ret = NSCAPI::returnUNKNOWN;
271                }
272        }
273        response_message.SerializeToString(&reply);
274        return ret;
275}
276
277int DistributedClient::clp_handler_impl::submit(client::configuration::data_type data, const Plugin::SubmitRequestMessage &request_message, std::string &reply) {
278        const ::Plugin::Common_Header& request_header = request_message.header();
279        int ret = NSCAPI::returnUNKNOWN;
280        connection_data con = parse_header(request_header, data);
281        Plugin::SubmitResponseMessage response_message;
282        nscapi::functions::make_return_header(response_message.mutable_header(), request_header);
283
284        std::list<nscp::packet> chunks;
285        chunks.push_back(nscp::factory::create_payload(nscp::data::command_response, request_message.SerializeAsString(), 0));
286        chunks = instance->send(con, chunks);
287        BOOST_FOREACH(nscp::packet &chunk, chunks) {
288                if (nscp::checks::is_submit_response(chunk)) {
289                        nscapi::functions::append_response_payloads(response_message, chunk.payload);
290                } else if (nscp::checks::is_error(chunk)) {
291                        std::string error = gather_and_log_errors(chunk.payload);
292                        nscapi::functions::append_simple_submit_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, error);
293                        ret = NSCAPI::returnUNKNOWN;
294                } else {
295                        NSC_LOG_ERROR_STD(_T("Unsupported message type: ") + strEx::itos(chunk.signature.payload_type));
296                        nscapi::functions::append_simple_submit_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, "Unsupported response type");
297                        ret = NSCAPI::returnUNKNOWN;
298                }
299        }
300        response_message.SerializeToString(&reply);
301        return ret;
302}
303
304int DistributedClient::clp_handler_impl::exec(client::configuration::data_type data, const Plugin::ExecuteRequestMessage &request_message, std::string &reply) {
305        const ::Plugin::Common_Header& request_header = request_message.header();
306        int ret = NSCAPI::returnOK;
307        connection_data con = parse_header(request_header, data);
308
309        Plugin::ExecuteResponseMessage response_message;
310        nscapi::functions::make_return_header(response_message.mutable_header(), request_header);
311
312        std::list<nscp::packet> chunks;
313        chunks.push_back(nscp::factory::create_envelope_request(1));
314        chunks.push_back(nscp::factory::create_payload(nscp::data::exec_request, request_message.SerializeAsString(), 0));
315        chunks = instance->send(con, chunks);
316        BOOST_FOREACH(nscp::packet &chunk, chunks) {
317                if (nscp::checks::is_exec_response(chunk)) {
318                        nscapi::functions::append_response_payloads(response_message, chunk.payload);
319                } else if (nscp::checks::is_error(chunk)) {
320                        std::string error = gather_and_log_errors(chunk.payload);
321                        nscapi::functions::append_simple_exec_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, error);
322                        ret = NSCAPI::returnUNKNOWN;
323                } else {
324                        NSC_LOG_ERROR_STD(_T("Unsupported message type: ") + strEx::itos(chunk.signature.payload_type));
325                        nscapi::functions::append_simple_exec_response_payload(response_message.add_payload(), "", NSCAPI::returnUNKNOWN, "Unsupported response type");
326                        ret = NSCAPI::returnUNKNOWN;
327                }
328        }
329        response_message.SerializeToString(&reply);
330        return ret;
331}
332
333//////////////////////////////////////////////////////////////////////////
334// Protocol implementations
335//
336
337std::list<nscp::packet> DistributedClient::send(connection_data &data, std::list<nscp::packet> &chunks) {
338        std::list<nscp::packet> tmp, result;
339        // @todo: fix this (take from header)
340        /*
341        std::wstring host = generic_data->host;
342        if (host.empty() && !generic_data->target.empty()) {
343        nscapi::target_handler::optarget t = targets.find_target(generic_data->target);
344        if (t)
345        host = (*t).host;
346        }
347        */
348        return send_nossl(data.address, data.timeout, chunks);
349}
350
351std::list<nscp::packet> DistributedClient::send_nossl(std::string host, int timeout, const std::list<nscp::packet> &chunks) {
352        zmq::context_t context(1);
353        zeromq::zeromq_client_handshake_reader handshaker("0987654321", nscp::factory::create_message_envelope_request(0));
354        zeromq::zeromq_client client(zeromq::zeromq_client::connection_info(host, timeout), &handshaker, &context);
355        client.connect();
356        std::list<nscp::packet> responses;
357        NSC_DEBUG_MSG_STD(_T("Sending data: ") + to_wstring(chunks.size()));
358        BOOST_FOREACH(const nscp::packet packet, chunks) {
359                zeromq::zeromq_client_message_reader reader(&handshaker, packet);
360                if (client.send(&reader)) {
361                        NSC_DEBUG_MSG(_T("Got response: ") + to_wstring(reader.response.signature.payload_type));
362                        responses.push_back(reader.response);
363                } else {
364                        NSC_LOG_ERROR(_T("Failed to read response!"));
365                        responses.push_back(nscp::factory::create_error(_T("Failed to send data to server.")));
366                }
367
368        }
369        return responses;
370}
371
372NSC_WRAP_DLL();
373NSC_WRAPPERS_MAIN_DEF(DistributedClient);
374NSC_WRAPPERS_IGNORE_MSG_DEF();
375NSC_WRAPPERS_HANDLE_CMD_DEF();
376NSC_WRAPPERS_CLI_DEF();
377NSC_WRAPPERS_HANDLE_NOTIFICATION_DEF();
378
Note: See TracBrowser for help on using the repository browser.