source: nscp/NSClient++.cpp @ 8223547

0.4.00.4.10.4.2stable
Last change on this file since 8223547 was 8223547, checked in by Michael Medin <michael@…>, 8 years ago
  • Improved thread safety in Core + Added exception handling and logging in PDH code
  • Various fixes all over the place + New module for check size of files/directories
  • Changed build to Dynamic link
  • Added regular expression support for EventLog? checker
  • Property mode set to 100644
File size: 12.6 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                std::cout << "Exception raised: " << e->error_ << " in module: " << e->file_ << std::endl;;
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,20000);
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        {
177                MutexLock lock2(messageMutex,20000);
178                if (!lock2.hasMutex()) {
179                        LOG_ERROR("FATAL ERROR: Could not get mutex (we will now crash BTW).");
180                } else {
181                        messageHandlers_.clear();
182                }
183        }
184        commandHandlers_.clear();
185        for (it = plugins_.rbegin(); it != plugins_.rend(); ++it) {
186                delete (*it);
187        }
188        plugins_.clear();
189}
190/**
191 * Load a single plug-in using a DLL filename
192 * @param file The DLL file
193 */
194void NSClientT::loadPlugin(const std::string file) {
195        addPlugin(new NSCPlugin(file));
196}
197/**
198 * Load and add a plugin to various internal structures
199 * @param *plugin The plug-ininstance to load. The pointer is managed by the
200 */
201void NSClientT::addPlugin(plugin_type plugin) {
202        MutexLock lock(pluginMutex);
203        if (!lock.hasMutex()) {
204                LOG_ERROR("FATAL ERROR: Could not get mutex.");
205                return;
206        }
207        plugin->load();
208        LOG_DEBUG_STD("Loading: " + plugin->getName());
209        // @todo Catch here and unload if we fail perhaps ?
210        plugins_.push_back(plugin);
211        if (plugin->hasCommandHandler())
212                commandHandlers_.push_back(plugin);
213        if (plugin->hasMessageHandler())
214                messageHandlers_.push_back(plugin);
215}
216/**
217 * Inject a command into the plug-in stack.
218 *
219 * @param buffer A command string to inject. This should be a unparsed command string such as command&arg1&arg2&arg...
220 * @return The result, empty string if no result
221 */
222std::string NSClientT::inject(const std::string buffer) {
223        std::list<std::string> args = charEx::split(buffer.c_str(), '&');
224        if (args.empty())
225                return "";
226        std::string command = args.front(); args.pop_front();
227        LOG_MESSAGE_STD("Injecting: " + command);
228        std::string ret = execute(NSClientT::getPassword(), command, args);
229        LOG_MESSAGE_STD("Injected Result: " + ret);
230        return ret;
231
232}
233/**
234 * Helper function to return the current password (perhaps this should be static ?)
235 *
236 * @return The current password
237 */
238std::string NSClientT::getPassword() {
239        return Settings::getInstance()->getString("generic", "password", "");
240}
241/**
242 * Execute a command.
243 *
244 * @param password The password
245 * @param cmd The command
246 * @param args Arguments as a list<string>
247 * @return The result if any, empty string if no result.
248 *
249 * @todo Make an int return value to set critical/warning/ok/unknown status.
250 * ie. pair<int,string>
251 */
252std::string NSClientT::execute(std::string password, std::string cmd, std::list<std::string> args) {
253        MutexLock lock(pluginMutex);
254        if (!lock.hasMutex()) {
255                LOG_ERROR("FATAL ERROR: Could not execute command with a reasonable timeframe. (could not get mutex so core is most likely locked down).");
256                return "ERROR: Core was locked down";
257        }
258        static unsigned int bufferSize = 0;
259        if (bufferSize == 0)
260                bufferSize = static_cast<unsigned int>(Settings::getInstance()->getInt("main", "bufferSize", 4096));
261
262        if (password != getPassword())
263                return "ERROR: Authorization denied.";
264
265        std::string ret;
266        unsigned int len;
267        char **arguments = NSCHelper::list2arrayBuffer(args, len);
268        // Allocate return buffer
269        char* returnbuffer = new char[bufferSize+1];
270        pluginList::const_iterator plit;
271        for (plit = commandHandlers_.begin(); plit != commandHandlers_.end(); ++plit) {
272                try {
273                        int c = (*plit)->handleCommand(cmd.c_str(), len, arguments, returnbuffer, bufferSize);
274                        if (c == NSCAPI::handled) {                                     // module handled the message "we are done..."
275                                ret = returnbuffer;
276                                break;
277                        } else if (c == NSCAPI::isfalse) {                      // Module ignored the message
278                                LOG_DEBUG("A module ignored this message");
279                        } else if (c == NSCAPI::invalidBufferLen) {     // Buffer is to small
280                                LOG_ERROR("Return buffer to small, need to increase it in the ini file.");
281                        } else {                                                                        // Something else went wrong...
282                                LOG_ERROR_STD("Unknown error from handleCommand: " + strEx::itos(c));
283                        }
284                } catch(const NSPluginException& e) {
285                        LOG_ERROR_STD("Exception raised: " + e.error_ + " in module: " + e.file_);
286                }
287        }
288        // delete buffers
289        delete [] returnbuffer;
290        NSCHelper::destroyArrayBuffer(arguments, len);
291        return ret;
292}
293/**
294 * Report a message to all logging enabled modules.
295 *
296 * @param msgType Message type
297 * @param file Filename generally __FILE__
298 * @param line  Line number, generally __LINE__
299 * @param message The message as a human readable string.
300 */
301void NSClientT::reportMessage(int msgType, const char* file, const int line, std::string message) {
302        MutexLock lock(messageMutex);
303        if (!lock.hasMutex()) {
304                std::cout << "Message was lost as the core was locked..." << std::endl;
305                std::cout << message << std::endl;
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 << "Caught: " << e.error_ << " when trying to log a message..." << std::endl;
327                        std::cout << "This is *really really* bad, now the world is about to end..." << std::endl;
328                }
329        }
330}
331std::string NSClientT::getBasePath(void) {
332        MutexLock lock(pluginMutex);
333        if (!lock.hasMutex()) {
334                LOG_ERROR("FATAL ERROR: Could not get mutex.");
335                return "FATAL ERROR";
336        }
337        if (!basePath.empty())
338                return basePath;
339        char* buffer = new char[1024];
340        GetModuleFileName(NULL, buffer, 1023);
341        std::string path = buffer;
342        std::string::size_type pos = path.rfind('\\');
343        basePath = path.substr(0, pos) + "\\";
344        delete [] buffer;
345        Settings::getInstance()->setFile(basePath + "NSC.ini");
346        return basePath;
347}
348
349
350int NSAPIGetSettingsString(const char* section, const char* key, const char* defaultValue, char* buffer, unsigned int bufLen) {
351        return NSCHelper::wrapReturnString(buffer, bufLen, Settings::getInstance()->getString(section, key, defaultValue));
352}
353int NSAPIGetSettingsInt(const char* section, const char* key, int defaultValue) {
354        return Settings::getInstance()->getInt(section, key, defaultValue);
355}
356int NSAPIGetBasePath(char*buffer, unsigned int bufLen) {
357        return NSCHelper::wrapReturnString(buffer, bufLen, mainClient.getBasePath());
358}
359int NSAPIGetApplicationName(char*buffer, unsigned int bufLen) {
360        return NSCHelper::wrapReturnString(buffer, bufLen, SZAPPNAME);
361}
362int NSAPIGetApplicationVersionStr(char*buffer, unsigned int bufLen) {
363        return NSCHelper::wrapReturnString(buffer, bufLen, SZVERSION);
364}
365void NSAPIMessage(int msgType, const char* file, const int line, const char* message) {
366        mainClient.reportMessage(msgType, file, line, message);
367}
368void NSAPIStopServer(void) {
369        serviceControll::Stop(SZSERVICENAME);
370}
371int NSAPIInject(const char* command, char* buffer, unsigned int bufLen) {
372        return NSCHelper::wrapReturnString(buffer, bufLen, mainClient.inject(command));
373}
374
375LPVOID NSAPILoader(char*buffer) {
376        if (stricmp(buffer, "NSAPIGetApplicationName") == 0)
377                return &NSAPIGetApplicationName;
378        if (stricmp(buffer, "NSAPIGetApplicationVersionStr") == 0)
379                return &NSAPIGetApplicationVersionStr;
380        if (stricmp(buffer, "NSAPIGetSettingsString") == 0)
381                return &NSAPIGetSettingsString;
382        if (stricmp(buffer, "NSAPIGetSettingsInt") == 0)
383                return &NSAPIGetSettingsInt;
384        if (stricmp(buffer, "NSAPIMessage") == 0)
385                return &NSAPIMessage;
386        if (stricmp(buffer, "NSAPIStopServer") == 0)
387                return &NSAPIStopServer;
388        if (stricmp(buffer, "NSAPIInject") == 0)
389                return &NSAPIInject;
390        if (stricmp(buffer, "NSAPIGetBasePath") == 0)
391                return &NSAPIGetBasePath;
392        return NULL;
393}
Note: See TracBrowser for help on using the repository browser.