source: nscp/trunk/NSClient++.cpp @ f22f5a9

Last change on this file since f22f5a9 was f22f5a9, checked in by Michael Medin <michael@…>, 8 years ago
  • Fixed multitasking socket listsner (had a hard-to-find memory leak)
  • Changed ProcessEnumeration? class to one that should work on NT4 New option to set this in the config file. + Added some more options to the config file.
  • Property mode set to 100644
File size: 14.0 KB
Line 
1//////////////////////////////////////////////////////////////////////////
2// NSClient++ Base Service
3//
4// Copyright (c) 2004 MySolutions NORDIC (http://www.medin.name)
5//
6// Date: 2004-03-13
7// Author: Michael Medin (michael@medin.name)
8//
9// Part of this file is based on work by Bruno Vais (bvais@usa.net)
10//
11// This software is provided "AS IS", without a warranty of any kind.
12// You are free to use/modify this code but leave this header intact.
13//
14//////////////////////////////////////////////////////////////////////////
15#include "stdafx.h"
16#include <winsvc.h>
17#include "NSClient++.h"
18#include "Settings.h"
19#include <charEx.h>
20#include <Socket.h>
21
22NSClient mainClient;    // Global core instance.
23bool g_bConsoleLog = false;
24//////////////////////////////////////////////////////////////////////////
25// Startup code
26
27/**
28 * Application startup point
29 *
30 * @param argc Argument count
31 * @param argv[] Argument array
32 * @param envp[] Environment array
33 * @return exit status
34 */
35int main(int argc, TCHAR* argv[], TCHAR* envp[])
36{
37        int nRetCode = 0;
38
39        if ( (argc > 1) && ((*argv[1] == '-') || (*argv[1] == '/')) ) {
40                if ( _stricmp( "install", argv[1]+1 ) == 0 ) {
41                        g_bConsoleLog = true;
42                        try {
43                                serviceControll::Install(SZSERVICENAME, SZSERVICEDISPLAYNAME, SZDEPENDENCIES);
44                        } catch (const serviceControll::SCException& e) {
45                                LOG_MESSAGE_STD("Service installation failed: " + e.error_);
46                                return -1;
47                        }
48                        LOG_MESSAGE("Service installed!");
49                } else if ( _stricmp( "uninstall", argv[1]+1 ) == 0 ) {
50                        g_bConsoleLog = true;
51                        try {
52                                serviceControll::Uninstall(SZSERVICENAME);
53                        } catch (const serviceControll::SCException& e) {
54                                LOG_MESSAGE_STD("Service deinstallation failed; " + e.error_);
55                                return -1;
56                        }
57                } else if ( _stricmp( "start", argv[1]+1 ) == 0 ) {
58                        g_bConsoleLog = true;
59                        serviceControll::Start(SZSERVICENAME);
60                } else if ( _stricmp( "stop", argv[1]+1 ) == 0 ) {
61                        g_bConsoleLog = true;
62                        serviceControll::Stop(SZSERVICENAME);
63                } else if ( _stricmp( "about", argv[1]+1 ) == 0 ) {
64                        g_bConsoleLog = true;
65                        LOG_MESSAGE(SZAPPNAME " (C) Michael Medin");
66                        LOG_MESSAGE("Version " SZVERSION);
67                } else if ( _stricmp( "version", argv[1]+1 ) == 0 ) {
68                        g_bConsoleLog = true;
69                        LOG_MESSAGE(SZAPPNAME " Version: " SZVERSION);
70                } else if ( _stricmp( "test", argv[1]+1 ) == 0 ) {
71#ifdef _DEBUG
72                        strEx::run_test_getToken();
73                        strEx::run_test_replace();
74                        charEx::run_test_getToken();
75                        arrayBuffer::run_testArrayBuffer();
76#endif
77
78                        g_bConsoleLog = true;
79                        mainClient.InitiateService();
80                        LOG_MESSAGE("Enter command to inject or exit to terminate...");
81                        std::string s = "";
82                        std::cin >> s;
83                        while (s != "exit") {
84                                strEx::token t = strEx::getToken(s, ',');
85                                std::string msg, perf;
86                                NSCAPI::nagiosReturn ret = mainClient.inject(t.first, t.second, ',', msg, perf);
87                                if (perf.empty())
88                                        std::cout << NSCHelper::translateReturn(ret) << ":" << msg << std::endl;
89                                else
90                                        std::cout << NSCHelper::translateReturn(ret) << ":" << msg << "|" << perf << std::endl;
91                                std::cin >> s;
92                        }
93                        mainClient.TerminateService();
94                        LOG_MESSAGE("DONE!");
95
96                        return 0;
97                } else {
98                        LOG_MESSAGE("Usage: -version, -about, -install, -uninstall, -start, -stop");
99                }
100                return nRetCode;
101        }
102        mainClient.StartServiceCtrlDispatcher();
103        return nRetCode;
104}
105
106//////////////////////////////////////////////////////////////////////////
107// Service functions
108
109/**
110 * Service control handler startup point.
111 * When the program is started as a service this will be the entry point.
112 */
113void NSClientT::InitiateService(void) {
114        Settings::getInstance()->setFile(getBasePath() + "NSC.ini");
115
116        try {
117                simpleSocket::WSAStartup();
118        } catch (simpleSocket::SocketException e) {
119                LOG_ERROR_STD("Uncaught exception: " + e.getMessage());
120        }
121
122        SettingsT::sectionList list = Settings::getInstance()->getSection("modules");
123        for (SettingsT::sectionList::iterator it = list.begin(); it != list.end(); it++) {
124                try {
125                        LOG_DEBUG_STD("Loading: " + getBasePath() + "modules\\" + (*it));
126                        loadPlugin(getBasePath() + "modules\\" + (*it));
127                } catch(const NSPluginException& e) {
128                        LOG_ERROR_STD("Exception raised: " + e.error_ + " in module: " + e.file_);
129                }
130        }
131}
132/**
133 * Service control handler termination point.
134 * When the program is stopped as a service this will be the "exit point".
135 */
136void NSClientT::TerminateService(void) {
137        try {
138                mainClient.unloadPlugins();
139                LOG_DEBUG("Plugins unloaded...");
140        } catch(NSPluginException *e) {
141                std::cout << "Exception raised: " << e->error_ << " in module: " << e->file_ << std::endl;;
142        }
143        try {
144                simpleSocket::WSACleanup();
145        } catch (simpleSocket::SocketException e) {
146                LOG_ERROR_STD("Uncaught exception: " + e.getMessage());
147        }
148        Settings::destroyInstance();
149}
150
151/**
152 * Forward this to the main service dispatcher helper class
153 * @param dwArgc
154 * @param *lpszArgv
155 */
156void WINAPI NSClientT::service_main_dispatch(DWORD dwArgc, LPTSTR *lpszArgv) {
157        mainClient.service_main(dwArgc, lpszArgv);
158}
159/**
160 * Forward this to the main service dispatcher helper class
161 * @param dwCtrlCode
162 */
163void WINAPI NSClientT::service_ctrl_dispatch(DWORD dwCtrlCode) {
164        mainClient.service_ctrl(dwCtrlCode);
165}
166
167//////////////////////////////////////////////////////////////////////////
168// Member functions
169
170
171/**
172 * Load a list of plug-ins
173 * @param plugins A list with plug-ins (DLL files) to load
174 */
175void NSClientT::loadPlugins(const std::list<std::string> plugins) {
176        MutexLock lock(pluginMutex);
177        if (!lock.hasMutex()) {
178                LOG_ERROR("FATAL ERROR: Could not get mutex.");
179                return;
180        }
181        std::list<std::string>::const_iterator it;
182        for (it = plugins.begin(); it != plugins.end(); ++it) {
183                loadPlugin(*it);
184        }
185}
186/**
187 * Unload all plug-ins (in reversed order)
188 */
189void NSClientT::unloadPlugins() {
190        MutexLock lock(pluginMutex,20000);
191        if (!lock.hasMutex()) {
192                LOG_ERROR("FATAL ERROR: Could not get mutex.");
193                return;
194        }
195        commandHandlers_.clear();
196        {
197                MutexLock lock2(messageMutex,20000);
198                if (!lock2.hasMutex()) {
199                        LOG_ERROR("FATAL ERROR: Could not get mutex (we will now crash BTW).");
200                } else {
201                        messageHandlers_.clear();
202                }
203        }
204        for (pluginList::size_type i=plugins_.size();i>0;i--) {
205                NSCPlugin *p = plugins_[i-1];
206                LOG_DEBUG_STD("Unloading plugin: " + p->getName() + "...");
207                p->unload();
208        }
209
210        for (unsigned int i=plugins_.size();i>0;i--) {
211                NSCPlugin *p = plugins_[i-1];
212                plugins_[i-1] = NULL;
213                delete p;
214        }
215        plugins_.clear();
216}
217/**
218 * Load a single plug-in using a DLL filename
219 * @param file The DLL file
220 */
221void NSClientT::loadPlugin(const std::string file) {
222        addPlugin(new NSCPlugin(file));
223}
224/**
225 * Load and add a plugin to various internal structures
226 * @param *plugin The plug-ininstance to load. The pointer is managed by the
227 */
228void NSClientT::addPlugin(plugin_type plugin) {
229        MutexLock lock(pluginMutex);
230        if (!lock.hasMutex()) {
231                LOG_ERROR("FATAL ERROR: Could not get mutex.");
232                return;
233        }
234        plugin->load();
235        LOG_DEBUG_STD("Loading: " + plugin->getName());
236        // @todo Catch here and unload if we fail perhaps ?
237        plugins_.insert(plugins_.end(), plugin);
238        if (plugin->hasCommandHandler())
239                commandHandlers_.insert(commandHandlers_.end(), plugin);
240        if (plugin->hasMessageHandler())
241                messageHandlers_.insert(messageHandlers_.end(), plugin);
242}
243
244NSCAPI::nagiosReturn NSClientT::inject(std::string command, std::string arguments, char splitter, std::string &msg, std::string & perf) {
245        unsigned int aLen = 0;
246        char ** aBuf = arrayBuffer::split2arrayBuffer(arguments, splitter, aLen);
247        char * mBuf = new char[1024];
248        char * pBuf = new char[1024];
249        NSCAPI::nagiosReturn ret = injectRAW(command.c_str(), aLen, aBuf, mBuf, 1023, pBuf, 1023);
250        arrayBuffer::destroyArrayBuffer(aBuf, aLen);
251        if ( (ret == NSCAPI::returnInvalidBufferLen) || (ret == NSCAPI::returnIgnored) )
252                return ret;
253        msg = mBuf;
254        perf = pBuf;
255        delete [] mBuf;
256        delete [] pBuf;
257        return ret;
258}
259
260/**
261 * Inject a command into the plug-in stack.
262 *
263 * @param buffer A command string to inject. This should be a unparsed command string such as command&arg1&arg2&arg...
264 * @return The result, empty string if no result
265 */
266NSCAPI::nagiosReturn NSClientT::injectRAW(const char* command, const unsigned int argLen, char **argument, char *returnMessageBuffer, unsigned int returnMessageBufferLen, char *returnPerfBuffer, unsigned int returnPerfBufferLen) {
267        MutexLock lock(pluginMutex);
268        if (!lock.hasMutex()) {
269                LOG_ERROR_STD("Failed to get mutex (" + strEx::itos(lock.getWaitResult()) + "), command ignored...");
270                return NSCAPI::returnUNKNOWN;
271        }
272
273        for (pluginList::size_type i = 0; i < commandHandlers_.size(); i++) {
274                try {
275                        NSCAPI::nagiosReturn c = commandHandlers_[i]->handleCommand(command, argLen, argument, returnMessageBuffer, returnMessageBufferLen, returnPerfBuffer, returnPerfBufferLen);
276                        switch (c) {
277                                case NSCAPI::returnInvalidBufferLen:
278                                        LOG_ERROR("Return buffer to small to handle this command.");
279                                        return c;
280                                case NSCAPI::returnIgnored:
281                                        break;
282                                case NSCAPI::returnOK:
283                                case NSCAPI::returnWARN:
284                                case NSCAPI::returnCRIT:
285                                case NSCAPI::returnUNKNOWN:
286                                        LOG_DEBUG_STD("Injected Result: " +(std::string) returnMessageBuffer);
287                                        LOG_DEBUG_STD("Injected Performance Result: " +(std::string) returnPerfBuffer);
288                                        return c;
289                                default:
290                                        LOG_ERROR_STD("Unknown error from handleCommand: " + strEx::itos(c));
291                                        return c;
292                        }
293                } catch(const NSPluginException& e) {
294                        LOG_ERROR_STD("Exception raised: " + e.error_ + " in module: " + e.file_);
295                        return NSCAPI::returnCRIT;
296                }
297        }
298        LOG_MESSAGE_STD("No handler for command: " + command);
299        return NSCAPI::returnIgnored;
300}
301/**
302 * Report a message to all logging enabled modules.
303 *
304 * @param msgType Message type
305 * @param file Filename generally __FILE__
306 * @param line  Line number, generally __LINE__
307 * @param message The message as a human readable string.
308 */
309void NSClientT::reportMessage(int msgType, const char* file, const int line, std::string message) {
310        MutexLock lock(messageMutex);
311        if (!lock.hasMutex()) {
312                std::cout << "Message was lost as the core was locked..." << std::endl;
313                std::cout << message << std::endl;
314                return;
315        }
316        if (g_bConsoleLog) {
317                std::string k = "?";
318                switch (msgType) {
319                        case NSCAPI::critical:
320                                k ="c";
321                                break;
322                        case NSCAPI::warning:
323                                k ="w";
324                                break;
325                        case NSCAPI::error:
326                                k ="e";
327                                break;
328                        case NSCAPI::log:
329                                k ="l";
330                                break;
331                        case NSCAPI::debug:
332                                k ="d";
333                                break;
334                }
335                std::cout << k << " " << file << "(" << line << ") " << message << std::endl;
336        }
337        if (msgType == NSCAPI::debug) {
338                typedef enum status {unknown, debug, nodebug };
339                static status d = unknown;
340                if (d == unknown) {
341                        if (Settings::getInstance()->getInt("log", "debug", 0) == 1)
342                                d = debug;
343                        else
344                                d = nodebug;
345                }
346                if (d == nodebug)
347                        return;
348        }
349        for (pluginList::size_type i = 0; i< messageHandlers_.size(); i++) {
350                try {
351                        messageHandlers_[i]->handleMessage(msgType, file, line, message.c_str());
352                } catch(const NSPluginException& e) {
353                        // Here we are pretty much fucked! (as logging this might cause a loop :)
354                        std::cout << "Caught: " << e.error_ << " when trying to log a message..." << std::endl;
355                        std::cout << "This is *really really* bad, now the world is about to end..." << std::endl;
356                }
357        }
358}
359std::string NSClientT::getBasePath(void) {
360        MutexLock lock(pluginMutex);
361        if (!lock.hasMutex()) {
362                LOG_ERROR("FATAL ERROR: Could not get mutex.");
363                return "FATAL ERROR";
364        }
365        if (!basePath.empty())
366                return basePath;
367        char* buffer = new char[1024];
368        GetModuleFileName(NULL, buffer, 1023);
369        std::string path = buffer;
370        std::string::size_type pos = path.rfind('\\');
371        basePath = path.substr(0, pos) + "\\";
372        delete [] buffer;
373        Settings::getInstance()->setFile(basePath + "NSC.ini");
374        return basePath;
375}
376
377
378NSCAPI::errorReturn NSAPIGetSettingsString(const char* section, const char* key, const char* defaultValue, char* buffer, unsigned int bufLen) {
379        return NSCHelper::wrapReturnString(buffer, bufLen, Settings::getInstance()->getString(section, key, defaultValue), NSCAPI::isSuccess);
380}
381int NSAPIGetSettingsInt(const char* section, const char* key, int defaultValue) {
382        return Settings::getInstance()->getInt(section, key, defaultValue);
383}
384NSCAPI::errorReturn NSAPIGetBasePath(char*buffer, unsigned int bufLen) {
385        return NSCHelper::wrapReturnString(buffer, bufLen, mainClient.getBasePath(), NSCAPI::isSuccess);
386}
387NSCAPI::errorReturn NSAPIGetApplicationName(char*buffer, unsigned int bufLen) {
388        return NSCHelper::wrapReturnString(buffer, bufLen, SZAPPNAME, NSCAPI::isSuccess);
389}
390NSCAPI::errorReturn NSAPIGetApplicationVersionStr(char*buffer, unsigned int bufLen) {
391        return NSCHelper::wrapReturnString(buffer, bufLen, SZVERSION, NSCAPI::isSuccess);
392}
393void NSAPIMessage(int msgType, const char* file, const int line, const char* message) {
394        mainClient.reportMessage(msgType, file, line, message);
395}
396void NSAPIStopServer(void) {
397        serviceControll::Stop(SZSERVICENAME);
398}
399NSCAPI::nagiosReturn NSAPIInject(const char* command, const unsigned int argLen, char **argument, char *returnMessageBuffer, unsigned int returnMessageBufferLen, char *returnPerfBuffer, unsigned int returnPerfBufferLen) {
400        return mainClient.injectRAW(command, argLen, argument, returnMessageBuffer, returnMessageBufferLen, returnPerfBuffer, returnPerfBufferLen);
401}
402NSCAPI::errorReturn NSAPIGetSettingsSection(const char* section, char*** aBuffer, unsigned int * bufLen) {
403        unsigned int len = 0;
404        *aBuffer = arrayBuffer::list2arrayBuffer(Settings::getInstance()->getSection(section), len);
405        *bufLen = len;
406        return NSCAPI::isSuccess;
407}
408
409
410LPVOID NSAPILoader(char*buffer) {
411        if (stricmp(buffer, "NSAPIGetApplicationName") == 0)
412                return &NSAPIGetApplicationName;
413        if (stricmp(buffer, "NSAPIGetApplicationVersionStr") == 0)
414                return &NSAPIGetApplicationVersionStr;
415        if (stricmp(buffer, "NSAPIGetSettingsSection") == 0)
416                return &NSAPIGetSettingsSection;
417        if (stricmp(buffer, "NSAPIGetSettingsString") == 0)
418                return &NSAPIGetSettingsString;
419        if (stricmp(buffer, "NSAPIGetSettingsInt") == 0)
420                return &NSAPIGetSettingsInt;
421        if (stricmp(buffer, "NSAPIMessage") == 0)
422                return &NSAPIMessage;
423        if (stricmp(buffer, "NSAPIStopServer") == 0)
424                return &NSAPIStopServer;
425        if (stricmp(buffer, "NSAPIInject") == 0)
426                return &NSAPIInject;
427        if (stricmp(buffer, "NSAPIGetBasePath") == 0)
428                return &NSAPIGetBasePath;
429        return NULL;
430}
Note: See TracBrowser for help on using the repository browser.