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

Last change on this file since f7f536b was f7f536b, checked in by Michael Medin <michael@…>, 8 years ago

Multiple fixes in various places.

  • Added threadding blocks "core"
  • Added new Module (CheckDisk)
  • Added new option [log] / debug=1 to enable debug logs.
  • Added more error messages
  • other minor tweaks and fixes
  • Property mode set to 100644
File size: 12.4 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
21NSClient mainClient;    // Global core instance.
22
23//////////////////////////////////////////////////////////////////////////
24// Startup code
25
26/**
27 * Application startup point
28 *
29 * @param argc Argument count
30 * @param argv[] Argument array
31 * @param envp[] Environment array
32 * @return exit status
33 */
34int main(int argc, TCHAR* argv[], TCHAR* envp[])
35{
36        int nRetCode = 0;
37
38        if ( (argc > 1) && ((*argv[1] == '-') || (*argv[1] == '/')) ) {
39                if ( _stricmp( "install", argv[1]+1 ) == 0 ) {
40                        try {
41                                serviceControll::Install(SZSERVICENAME, SZSERVICEDISPLAYNAME, SZDEPENDENCIES);
42                        } catch (const serviceControll::SCException& e) {
43                                LOG_MESSAGE_STD("Service installation failed: " + e.error_);
44                                return -1;
45                        }
46                        LOG_MESSAGE("Service installed!");
47                } else if ( _stricmp( "uninstall", argv[1]+1 ) == 0 ) {
48                        try {
49                                serviceControll::Uninstall(SZSERVICENAME);
50                        } catch (const serviceControll::SCException& e) {
51                                LOG_MESSAGE_STD("Service deinstallation failed; " + e.error_);
52                                return -1;
53                        }
54                } else if ( _stricmp( "start", argv[1]+1 ) == 0 ) {
55                        serviceControll::Start(SZSERVICENAME);
56                } else if ( _stricmp( "stop", argv[1]+1 ) == 0 ) {
57                        serviceControll::Stop(SZSERVICENAME);
58                } else if ( _stricmp( "about", argv[1]+1 ) == 0 ) {
59                        LOG_MESSAGE(SZAPPNAME " (C) Michael Medin");
60                        LOG_MESSAGE("Version " SZVERSION);
61                } else if ( _stricmp( "version", argv[1]+1 ) == 0 ) {
62                        LOG_MESSAGE(SZAPPNAME " Version: " SZVERSION);
63                } else if ( _stricmp( "test", argv[1]+1 ) == 0 ) {
64                        mainClient.InitiateService();
65                        LOG_MESSAGE("Enter command to inject or exit to terminate...");
66                        std::string s = "";
67                        std::cin >> s;
68                        while (s != "exit") {
69                                mainClient.inject(s);
70                                std::cin >> s;
71                        }
72                        mainClient.TerminateService();
73                        LOG_MESSAGE("DONE!");
74                        return 0;
75                } else {
76                        LOG_MESSAGE("Usage: -version, -about, -install, -uninstall, -start, -stop");
77                }
78                return nRetCode;
79        }
80        mainClient.StartServiceCtrlDispatcher();
81        return nRetCode;
82}
83
84//////////////////////////////////////////////////////////////////////////
85// Service functions
86
87/**
88 * Service control handler startup point.
89 * When the program is started as a service this will be the entry point.
90 */
91void NSClientT::InitiateService(void) {
92        Settings::getInstance()->setFile(getBasePath() + "NSC.ini");
93
94        SettingsT::sectionList list = Settings::getInstance()->getSection("modules");
95        for (SettingsT::sectionList::iterator it = list.begin(); it != list.end(); it++) {
96                try {
97                        LOG_DEBUG_STD("Loading: " + getBasePath() + "modules\\" + (*it));
98                        loadPlugin(getBasePath() + "modules\\" + (*it));
99                } catch(const NSPluginException& e) {
100                        LOG_ERROR_STD("Exception raised: " + e.error_ + " in module: " + e.file_);
101                }
102        }
103        socketThread.createThread();
104}
105/**
106 * Service control handler termination point.
107 * When the program is stopped as a service this will be the "exit point".
108 */
109void NSClientT::TerminateService(void) {
110        if (!socketThread.exitThread())
111                LOG_ERROR("Could not exit socket listener thread");
112        try {
113                LOG_DEBUG("Socket closed, unloading plugins...");
114                mainClient.unloadPlugins();
115                LOG_DEBUG("Plugins unloaded...");
116        } catch(NSPluginException *e) {
117                LOG_ERROR_STD("Exception raised: " + e->error_ + " in module: " + e->file_);
118        }
119        Settings::destroyInstance();
120}
121
122/**
123 * Forward this to the main service dispatcher helper class
124 * @param dwArgc
125 * @param *lpszArgv
126 */
127void WINAPI NSClientT::service_main_dispatch(DWORD dwArgc, LPTSTR *lpszArgv) {
128        mainClient.service_main(dwArgc, lpszArgv);
129}
130/**
131 * Forward this to the main service dispatcher helper class
132 * @param dwCtrlCode
133 */
134void WINAPI NSClientT::service_ctrl_dispatch(DWORD dwCtrlCode) {
135        mainClient.service_ctrl(dwCtrlCode);
136}
137
138//////////////////////////////////////////////////////////////////////////
139// Member functions
140
141
142/**
143 * Load a list of plug-ins
144 * @param plugins A list with plug-ins (DLL files) to load
145 */
146void NSClientT::loadPlugins(const std::list<std::string> plugins) {
147        MutexLock lock(pluginMutex);
148        if (!lock.hasMutex()) {
149                LOG_ERROR("FATAL ERROR: Could not get mutex.");
150                return;
151        }
152        std::list<std::string>::const_iterator it;
153        for (it = plugins.begin(); it != plugins.end(); ++it) {
154                loadPlugin(*it);
155        }
156}
157/**
158 * Unload all plug-ins (in reversed order)
159 */
160void NSClientT::unloadPlugins() {
161        MutexLock lock(pluginMutex);
162        if (!lock.hasMutex()) {
163                LOG_ERROR("FATAL ERROR: Could not get mutex.");
164                return;
165        }
166        pluginList::reverse_iterator it;
167        for (it = plugins_.rbegin(); it != plugins_.rend(); ++it) {
168#ifdef _DEBUG
169                std::cout << "Unloading plugin: " << (*it)->getName() << "...";
170#endif
171                (*it)->unload();
172#ifdef _DEBUG
173                std::cout << "OK" << std::endl;
174#endif
175        }
176        messageHandlers_.clear();
177        commandHandlers_.clear();
178        for (it = plugins_.rbegin(); it != plugins_.rend(); ++it) {
179                delete (*it);
180        }
181        plugins_.clear();
182}
183/**
184 * Load a single plug-in using a DLL filename
185 * @param file The DLL file
186 */
187void NSClientT::loadPlugin(const std::string file) {
188        addPlugin(new NSCPlugin(file));
189}
190/**
191 * Load and add a plugin to various internal structures
192 * @param *plugin The plug-ininstance to load. The pointer is managed by the
193 */
194void NSClientT::addPlugin(plugin_type plugin) {
195        MutexLock lock(pluginMutex);
196        if (!lock.hasMutex()) {
197                LOG_ERROR("FATAL ERROR: Could not get mutex.");
198                return;
199        }
200        plugin->load();
201        LOG_DEBUG_STD("Loading: " + plugin->getName());
202        // @todo Catch here and unload if we fail perhaps ?
203        plugins_.push_back(plugin);
204        if (plugin->hasCommandHandler())
205                commandHandlers_.push_back(plugin);
206        if (plugin->hasMessageHandler())
207                messageHandlers_.push_back(plugin);
208}
209/**
210 * Inject a command into the plug-in stack.
211 *
212 * @param buffer A command string to inject. This should be a unparsed command string such as command&arg1&arg2&arg...
213 * @return The result, empty string if no result
214 */
215std::string NSClientT::inject(const std::string buffer) {
216        std::list<std::string> args = charEx::split(buffer.c_str(), '&');
217        std::string command = args.front(); args.pop_front();
218        LOG_MESSAGE_STD("Injecting: " + command);
219        std::string ret = execute(NSClientT::getPassword(), command, args);
220        LOG_MESSAGE_STD("Injected Result: " + ret);
221        return ret;
222
223}
224/**
225 * Helper function to return the current password (perhaps this should be static ?)
226 *
227 * @return The current password
228 */
229std::string NSClientT::getPassword() {
230        return Settings::getInstance()->getString("generic", "password", "");
231}
232/**
233 * Execute a command.
234 *
235 * @param password The password
236 * @param cmd The command
237 * @param args Arguments as a list<string>
238 * @return The result if any, empty string if no result.
239 *
240 * @todo Make an int return value to set critical/warning/ok/unknown status.
241 * ie. pair<int,string>
242 */
243std::string NSClientT::execute(std::string password, std::string cmd, std::list<std::string> args) {
244        MutexLock lock(pluginMutex);
245        if (!lock.hasMutex()) {
246                LOG_ERROR("FATAL ERROR: Could not get mutex.");
247                return "FATAL ERROR";
248        }
249        static unsigned int bufferSize = 0;
250        if (bufferSize == 0)
251                bufferSize = static_cast<unsigned int>(Settings::getInstance()->getInt("main", "bufferSize", 4096));
252
253        if (password != getPassword())
254                return "INVALID PASSWORD";
255
256        // Pack the argument as a char** buffer
257        std::string ret;
258        int len = args.size();
259        char **arguments = new char*[len];
260        std::list<std::string>::iterator it = args.begin();
261        for (int i=0;it!=args.end();++it,i++) {
262                int alen = (*it).size();
263                arguments[i] = new char[alen+2];
264                strncpy(arguments[i], (*it).c_str(), alen+1);
265        }
266        // Allocate return buffer
267        char* returnbuffer = new char[bufferSize+1];
268        pluginList::const_iterator plit;
269        for (plit = commandHandlers_.begin(); plit != commandHandlers_.end(); ++plit) {
270                try {
271                        int c = (*plit)->handleCommand(cmd.c_str(), len, arguments, returnbuffer, bufferSize);
272                        if (c == NSCAPI::handled) {                                     // module handled the message "we are done..."
273                                ret = returnbuffer;
274                                break;
275                        } else if (c == NSCAPI::isfalse) {                      // Module ignored the message
276                                LOG_DEBUG("A module ignored this message");
277                        } else if (c == NSCAPI::invalidBufferLen) {     // Buffer is to small
278                                LOG_ERROR("Return buffer to small, need to increase it in the ini file.");
279                        } else {                                                                        // Something else went wrong...
280                                LOG_ERROR_STD("Unknown error from handleCommand: " + strEx::itos(c));
281                        }
282                } catch(const NSPluginException& e) {
283                        LOG_ERROR_STD("Exception raised: " + e.error_ + " in module: " + e.file_);
284                }
285        }
286        // delete buffers
287        delete [] returnbuffer;
288        for (int i=0;i<len;i++) {
289                delete [] arguments[i];
290        }
291        delete [] arguments;
292        return ret;
293}
294/**
295 * Report a message to all logging enabled modules.
296 *
297 * @param msgType Message type
298 * @param file Filename generally __FILE__
299 * @param line  Line number, generally __LINE__
300 * @param message The message as a human readable string.
301 */
302void NSClientT::reportMessage(int msgType, const char* file, const int line, std::string message) {
303        MutexLock lock(messageMutex);
304        if (!lock.hasMutex()) {
305                LOG_ERROR("FATAL ERROR: Could not get mutex.");
306                return;
307        }
308        if (msgType == NSCAPI::debug) {
309                typedef enum status {unknown, debug, nodebug };
310                static status d = unknown;
311                if (d == unknown) {
312                        if (Settings::getInstance()->getInt("log", "debug", 0) == 1)
313                                d = debug;
314                        else
315                                d = nodebug;
316                }
317                if (d == nodebug)
318                        return;
319        }
320        pluginList::const_iterator plit;
321        for (plit = messageHandlers_.begin(); plit != messageHandlers_.end(); ++plit) {
322                try {
323                        (*plit)->handleMessage(msgType, file, line, message.c_str());
324                } catch(const NSPluginException& e) {
325                        // Here we are pretty much fucked! (as logging this might cause a loop :)
326                        std::cout << "This is *really really* bad, now the world is about to end..." << std::endl;
327                }
328        }
329}
330std::string NSClientT::getBasePath(void) {
331        MutexLock lock(pluginMutex);
332        if (!lock.hasMutex()) {
333                LOG_ERROR("FATAL ERROR: Could not get mutex.");
334                return "FATAL ERROR";
335        }
336        if (!basePath.empty())
337                return basePath;
338        char* buffer = new char[1024];
339        GetModuleFileName(NULL, buffer, 1023);
340        std::string path = buffer;
341        std::string::size_type pos = path.rfind('\\');
342        basePath = path.substr(0, pos) + "\\";
343        delete [] buffer;
344        Settings::getInstance()->setFile(basePath + "NSC.ini");
345        return basePath;
346}
347
348
349int NSAPIGetSettingsString(const char* section, const char* key, const char* defaultValue, char* buffer, unsigned int bufLen) {
350        return NSCHelper::wrapReturnString(buffer, bufLen, Settings::getInstance()->getString(section, key, defaultValue));
351}
352int NSAPIGetSettingsInt(const char* section, const char* key, int defaultValue) {
353        return Settings::getInstance()->getInt(section, key, defaultValue);
354}
355int NSAPIGetBasePath(char*buffer, unsigned int bufLen) {
356        return NSCHelper::wrapReturnString(buffer, bufLen, mainClient.getBasePath());
357}
358int NSAPIGetApplicationName(char*buffer, unsigned int bufLen) {
359        return NSCHelper::wrapReturnString(buffer, bufLen, SZAPPNAME);
360}
361int NSAPIGetApplicationVersionStr(char*buffer, unsigned int bufLen) {
362        return NSCHelper::wrapReturnString(buffer, bufLen, SZVERSION);
363}
364void NSAPIMessage(int msgType, const char* file, const int line, const char* message) {
365        mainClient.reportMessage(msgType, file, line, message);
366}
367void NSAPIStopServer(void) {
368        serviceControll::Stop(SZSERVICENAME);
369}
370int NSAPIInject(const char* command, char* buffer, unsigned int bufLen) {
371        return NSCHelper::wrapReturnString(buffer, bufLen, mainClient.inject(command));
372}
373
374LPVOID NSAPILoader(char*buffer) {
375        if (stricmp(buffer, "NSAPIGetApplicationName") == 0)
376                return &NSAPIGetApplicationName;
377        if (stricmp(buffer, "NSAPIGetApplicationVersionStr") == 0)
378                return &NSAPIGetApplicationVersionStr;
379        if (stricmp(buffer, "NSAPIGetSettingsString") == 0)
380                return &NSAPIGetSettingsString;
381        if (stricmp(buffer, "NSAPIGetSettingsInt") == 0)
382                return &NSAPIGetSettingsInt;
383        if (stricmp(buffer, "NSAPIMessage") == 0)
384                return &NSAPIMessage;
385        if (stricmp(buffer, "NSAPIStopServer") == 0)
386                return &NSAPIStopServer;
387        if (stricmp(buffer, "NSAPIInject") == 0)
388                return &NSAPIInject;
389        if (stricmp(buffer, "NSAPIGetBasePath") == 0)
390                return &NSAPIGetBasePath;
391        return NULL;
392}
Note: See TracBrowser for help on using the repository browser.