- Added "Service Dependencies" page
[public/netxms.git] / src / server / tools / nxconfig / WizardWorker.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Server Configurator for Windows
4 ** Copyright (C) 2005 Victor Kirhenshtein
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 Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** $module: WizardWorker.cpp
21 ** Worker thread for configuration wizard
22 **
23 **/
24
25 #include "stdafx.h"
26 #include "nxconfig.h"
27
28
29 //
30 // Global data
31 //
32
33 TCHAR g_szWizardErrorText[MAX_ERROR_TEXT] = _T("Completed successfully");
34
35
36 //
37 // Static data
38 //
39
40 static HWND m_hStatusWnd = NULL;
41
42
43 //
44 // Install event source
45 //
46
47 static BOOL InstallEventSource(TCHAR *pszPath)
48 {
49 HKEY hKey;
50 TCHAR szBuffer[256];
51 DWORD dwTypes = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
52
53 if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE,
54 "System\\CurrentControlSet\\Services\\EventLog\\System\\" CORE_EVENT_SOURCE,
55 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL))
56 {
57 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT,
58 _T("Unable to create registry key: %s"),
59 GetSystemErrorText(GetLastError(), szBuffer, 256));
60 return FALSE;
61 }
62
63 RegSetValueEx(hKey, _T("TypesSupported"), 0, REG_DWORD,(BYTE *)&dwTypes, sizeof(DWORD));
64 RegSetValueEx(hKey, _T("EventMessageFile"), 0, REG_EXPAND_SZ,
65 (BYTE *)pszPath, ((DWORD)_tcslen(pszPath) + 1) * sizeof(TCHAR));
66
67 RegCloseKey(hKey);
68 return TRUE;
69 }
70
71
72 //
73 // Install Windows service
74 //
75
76 static BOOL InstallService(WIZARD_CFG_INFO *pc)
77 {
78 SC_HANDLE hMgr, hService;
79 TCHAR szCmdLine[MAX_PATH * 2], *pszLogin, *pszPassword, szBuffer[256];
80 BOOL bResult = FALSE;
81
82 hMgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
83 if (hMgr == NULL)
84 {
85 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT,
86 _T("Cannot connect to Service Manager: %s"),
87 GetSystemErrorText(GetLastError(), szBuffer, 256));
88 return FALSE;
89 }
90
91 if (pc->m_szServiceLogin[0] == 0)
92 {
93 pszLogin = NULL;
94 pszPassword = NULL;
95 }
96 else
97 {
98 pszLogin = pc->m_szServiceLogin;
99 pszPassword = pc->m_szServicePassword;
100 }
101 _sntprintf(szCmdLine, MAX_PATH * 2, _T("\"%s\\bin\\netxmsd.exe\" --config \"%s\""),
102 pc->m_szInstallDir, pc->m_szConfigFile);
103 hService = CreateService(hMgr, CORE_SERVICE_NAME, _T("NetXMS Core"),
104 GENERIC_READ, SERVICE_WIN32_OWN_PROCESS,
105 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
106 szCmdLine, NULL, NULL, NULL, pszLogin, pszPassword);
107 if (hService == NULL)
108 {
109 DWORD dwCode = GetLastError();
110
111 if (dwCode == ERROR_SERVICE_EXISTS)
112 {
113 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT,
114 _T("Service named '") CORE_SERVICE_NAME _T("' already exist"));
115 }
116 else
117 {
118 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT,
119 _T("Cannot create service: %s"),
120 GetSystemErrorText(dwCode, szBuffer, 256));
121 }
122 }
123 else
124 {
125 CloseServiceHandle(hService);
126 bResult = TRUE;
127 _sntprintf(szCmdLine, MAX_PATH, _T("%s\\bin\\libnxsrv.dll"), pc->m_szInstallDir);
128 }
129
130 CloseServiceHandle(hMgr);
131
132 return bResult ? InstallEventSource(szCmdLine) : FALSE;
133 }
134
135
136 //
137 // Execute query and set error text
138 //
139
140 static BOOL DBQueryEx(DB_HANDLE hConn, TCHAR *pszQuery)
141 {
142 BOOL bResult;
143
144 bResult = DBQuery(hConn, pszQuery);
145 if (!bResult)
146 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT,
147 _T("SQL query failed:\n%s"), pszQuery);
148 return bResult;
149 }
150
151
152 //
153 // Write string value to configuration table
154 //
155
156 static BOOL WriteConfigStr(DB_HANDLE hConn, TCHAR *pszVar, TCHAR *pszValue,
157 BOOL bIsVisible, BOOL bNeedRestart)
158 {
159 DB_RESULT hResult;
160 TCHAR *pszEscValue, szQuery[1024];
161 BOOL bVarExist = FALSE;
162
163 if (_tcslen(pszVar) > 127)
164 return FALSE;
165
166 // Check for variable existence
167 _sntprintf(szQuery, 1024, _T("SELECT var_value FROM config WHERE var_name='%s'"), pszVar);
168 hResult = DBSelect(hConn, szQuery);
169 if (hResult != 0)
170 {
171 if (DBGetNumRows(hResult) > 0)
172 bVarExist = TRUE;
173 DBFreeResult(hResult);
174 }
175
176 // Create or update variable value
177 pszEscValue = EncodeSQLString(pszValue);
178 if (bVarExist)
179 _sntprintf(szQuery, 1024,
180 _T("UPDATE config SET var_value='%s' WHERE var_name='%s'"),
181 pszEscValue, pszVar);
182 else
183 _sntprintf(szQuery, 1024,
184 _T("INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('%s','%s',%d,%d)"),
185 pszVar, pszEscValue, bIsVisible, bNeedRestart);
186 free(pszEscValue);
187 return DBQueryEx(hConn, szQuery);
188 }
189
190
191 //
192 // Write integer value to configuration table
193 //
194
195 static BOOL WriteConfigInt(DB_HANDLE hConn, TCHAR *pszVar, int iValue,
196 BOOL bIsVisible, BOOL bNeedRestart)
197 {
198 TCHAR szBuffer[64];
199
200 _stprintf(szBuffer, _T("%d"), iValue);
201 return WriteConfigStr(hConn, pszVar, szBuffer, bIsVisible, bNeedRestart);
202 }
203
204
205 //
206 // Write unsigned long value to configuration table
207 //
208
209 static BOOL WriteConfigULong(DB_HANDLE hConn, TCHAR *pszVar, DWORD dwValue,
210 BOOL bIsVisible, BOOL bNeedRestart)
211 {
212 TCHAR szBuffer[64];
213
214 _stprintf(szBuffer, _T("%u"), dwValue);
215 return WriteConfigStr(hConn, pszVar, szBuffer, bIsVisible, bNeedRestart);
216 }
217
218
219 //
220 // Create configuration file
221 //
222
223 static BOOL CreateConfigFile(WIZARD_CFG_INFO *pc)
224 {
225 BOOL bResult = FALSE;
226 FILE *pf;
227 time_t t;
228
229 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Creating configuration file"));
230
231 pf = _tfopen(pc->m_szConfigFile, _T("w"));
232 if (pf != NULL)
233 {
234 t= time(NULL);
235 fprintf(pf, _T("#\n# NetXMS Server configuration file\n")
236 _T("# Created by NetXMS Server configuration wizard at %s#\n\n"),
237 _tctime(&t));
238 fprintf(pf, _T("LogFile = %s\n"), pc->m_bLogToSyslog ? _T("{syslog}") : pc->m_szLogFile);
239 fprintf(pf, _T("DBDriver = %s\n"), pc->m_szDBDriver);
240 if (pc->m_szDBDrvParams[0] != 0)
241 fprintf(pf, _T("DBDrvParams = %s\n"), pc->m_szDBDrvParams);
242 if (!_tcsicmp(pc->m_szDBDriver, _T("sqlite.ddr")))
243 {
244 fprintf(pf, _T("DBName = %s\\database\\%s\n"), pc->m_szInstallDir, pc->m_szDBName);
245 }
246 else
247 {
248 if (pc->m_szDBServer[0] != 0)
249 fprintf(pf, _T("DBServer = %s\n"), pc->m_szDBServer);
250 if ((pc->m_szDBName[0] != 0) && (_tcsicmp(pc->m_szDBDriver, _T("odbc.ddr"))))
251 fprintf(pf, _T("DBName = %s\n"), pc->m_szDBName);
252 if (pc->m_szDBLogin[0] != 0)
253 fprintf(pf, _T("DBLogin = %s\n"), pc->m_szDBLogin);
254 if (pc->m_szDBPassword[0] != 0)
255 fprintf(pf, _T("DBPassword = %s\n"), pc->m_szDBPassword);
256 }
257 fprintf(pf, _T("LogFailedSQLQueries = %s\n"), pc->m_bLogFailedSQLQueries ? _T("yes") : _T("no"));
258 fclose(pf);
259 bResult = TRUE;
260 }
261 else
262 {
263 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT, _T("Error creating file %s"), pc->m_szConfigFile);
264 }
265
266 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
267 return bResult;
268 }
269
270
271 //
272 // Create database in MySQL
273 //
274
275 static BOOL CreateDBMySQL(WIZARD_CFG_INFO *pc, DB_HANDLE hConn)
276 {
277 TCHAR szQuery[256];
278 BOOL bResult;
279
280 _stprintf(szQuery, _T("CREATE DATABASE %s"), pc->m_szDBName);
281 bResult = DBQueryEx(hConn, szQuery);
282
283 if (bResult)
284 {
285 _stprintf(szQuery, _T("GRANT ALL ON %s.* TO %s IDENTIFIED BY '%s'"),
286 pc->m_szDBName, pc->m_szDBLogin, pc->m_szDBPassword);
287 bResult = DBQueryEx(hConn, szQuery);
288
289 _stprintf(szQuery, _T("GRANT ALL ON %s.* TO %s@localhost IDENTIFIED BY '%s'"),
290 pc->m_szDBName, pc->m_szDBLogin, pc->m_szDBPassword);
291 bResult = DBQueryEx(hConn, szQuery);
292 }
293
294 if (bResult)
295 bResult = DBQueryEx(hConn, _T("FLUSH PRIVILEGES"));
296 return bResult;
297 }
298
299
300 //
301 // Create database in PostgreSQL
302 //
303
304 static BOOL CreateDBPostgreSQL(WIZARD_CFG_INFO *pc, DB_HANDLE hConn)
305 {
306 TCHAR szQuery[256];
307 BOOL bResult;
308
309 _stprintf(szQuery, _T("CREATE DATABASE %s"), pc->m_szDBName);
310 bResult = DBQueryEx(hConn, szQuery);
311
312 if (bResult)
313 {
314 _stprintf(szQuery, _T("CREATE USER %s WITH PASSWORD '%s'"),
315 pc->m_szDBLogin, pc->m_szDBPassword);
316 bResult = DBQueryEx(hConn, szQuery);
317 }
318
319 if (bResult)
320 {
321 _stprintf(szQuery, _T("GRANT ALL PRIVILEGES ON DATABASE %s TO %s"),
322 pc->m_szDBName, pc->m_szDBLogin);
323 bResult = DBQueryEx(hConn, szQuery);
324 }
325
326 return bResult;
327 }
328
329
330 //
331 // Create database in Microsoft SQL
332 //
333
334 static BOOL CreateDBMSSQL(WIZARD_CFG_INFO *pc, DB_HANDLE hConn)
335 {
336 TCHAR szQuery[512], *pszLogin;
337 BOOL bResult;
338
339 bResult = DBQueryEx(hConn, _T("USE master"));
340 if (bResult)
341 {
342 _stprintf(szQuery, _T("CREATE DATABASE %s"), pc->m_szDBName);
343 bResult = DBQueryEx(hConn, szQuery);
344 }
345
346 if (bResult)
347 {
348 _stprintf(szQuery, _T("USE %s"), pc->m_szDBName);
349 bResult = DBQueryEx(hConn, szQuery);
350 }
351
352 if (bResult)
353 {
354 if (!strcmp(pc->m_szDBLogin, _T("*")))
355 {
356 // Use Windows authentication
357 pszLogin = pc->m_szServiceLogin;
358 }
359 else
360 {
361 _sntprintf(szQuery, 512, _T("sp_addlogin @loginame = '%s', @passwd = '%s', @defdb = '%s'"),
362 pc->m_szDBLogin, pc->m_szDBPassword, pc->m_szDBName);
363 bResult = DBQueryEx(hConn, szQuery);
364 pszLogin = pc->m_szDBLogin;
365 }
366 }
367
368 if (bResult)
369 {
370 _sntprintf(szQuery, 512, _T("sp_grantdbaccess @loginame = '%s'"), pszLogin);
371 bResult = DBQueryEx(hConn, szQuery);
372 }
373
374 if (bResult)
375 {
376 _stprintf(szQuery, _T("GRANT ALL TO %s"), pszLogin);
377 bResult = DBQueryEx(hConn, szQuery);
378 }
379
380 return bResult;
381 }
382
383
384 //
385 // Create SQLite embedded database
386 //
387
388 static BOOL CreateSQLiteDB(WIZARD_CFG_INFO *pc)
389 {
390 TCHAR szBaseDir[MAX_PATH];
391 DB_HANDLE hConn;
392 BOOL bResult = FALSE;
393
394 _stprintf(szBaseDir, _T("%s\\database"), pc->m_szInstallDir);
395 SetCurrentDirectory(szBaseDir);
396 DeleteFile(pc->m_szDBName);
397 hConn = DBConnectEx(NULL, pc->m_szDBName, NULL, NULL);
398 if (hConn != NULL)
399 {
400 DBDisconnect(hConn);
401 bResult = TRUE;
402 }
403 return bResult;
404 }
405
406
407 //
408 // Create database
409 //
410
411 static BOOL CreateDatabase(WIZARD_CFG_INFO *pc)
412 {
413 DB_HANDLE hConn;
414 BOOL bResult = FALSE;
415
416 if (pc->m_iDBEngine == DB_ENGINE_SQLITE)
417 {
418 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Creating database"));
419 bResult = CreateSQLiteDB(pc);
420 }
421 else
422 {
423 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Connecting to database server as DBA"));
424 hConn = DBConnectEx(pc->m_szDBServer,
425 (pc->m_iDBEngine == DB_ENGINE_PGSQL) ? _T("template1") : NULL,
426 pc->m_szDBALogin, pc->m_szDBAPassword);
427 if (hConn != NULL)
428 {
429 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, TRUE, 0);
430 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Creating database"));
431
432 switch(pc->m_iDBEngine)
433 {
434 case DB_ENGINE_MYSQL:
435 bResult = CreateDBMySQL(pc, hConn);
436 break;
437 case DB_ENGINE_MSSQL:
438 bResult = CreateDBMSSQL(pc, hConn);
439 break;
440 case DB_ENGINE_PGSQL:
441 bResult = CreateDBPostgreSQL(pc, hConn);
442 break;
443 default:
444 bResult = FALSE;
445 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT,
446 _T("Unsupported database engine code %d"), pc->m_iDBEngine);
447 break;
448 }
449
450 DBDisconnect(hConn);
451 }
452 else
453 {
454 _tcscpy(g_szWizardErrorText, _T("Unable to connect to database"));
455 }
456 }
457
458 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
459 return bResult;
460 }
461
462
463 //
464 // Worker thread
465 //
466
467 static DWORD __stdcall WorkerThread(void *pArg)
468 {
469 WIZARD_CFG_INFO *pc = (WIZARD_CFG_INFO *)pArg;
470 DB_HANDLE hConn = NULL;
471 TCHAR szDataDir[MAX_PATH], szQuery[256], szGUID[64];
472 uuid_t guid;
473 BOOL bResult;
474
475 bResult = CreateConfigFile(pc);
476
477 // Load database driver
478 if (bResult)
479 {
480 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Loading database driver"));
481 nx_strncpy(g_szDbDriver, pc->m_szDBDriver, MAX_PATH);
482 nx_strncpy(g_szDbServer, pc->m_szDBServer, MAX_PATH);
483 bResult = DBInit(FALSE, FALSE, FALSE);
484 if (!bResult)
485 _tcscpy(g_szWizardErrorText, _T("Error loading database driver"));
486 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
487 }
488
489 // Create database if requested
490 if (pc->m_bCreateDB && bResult)
491 bResult = CreateDatabase(pc);
492
493 // Connect to database as user
494 if (bResult)
495 {
496 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Connecting to database"));
497 hConn = DBConnectEx(pc->m_szDBServer, pc->m_szDBName, pc->m_szDBLogin, pc->m_szDBPassword);
498 bResult = (hConn != NULL);
499 if (!bResult)
500 _tcscpy(g_szWizardErrorText, _T("Unable to connect to database"));
501 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
502 }
503
504 // Initialize database if requested
505 if ((pc->m_bCreateDB || pc->m_bInitDB) && bResult)
506 {
507 TCHAR szInitFile[MAX_PATH];
508 static TCHAR *szEngineName[] = { _T("mysql"), _T("pgsql"), _T("mssql"), _T("oracle"), _T("sqlite") };
509
510 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Initializing database"));
511
512 _sntprintf(szInitFile, MAX_PATH, _T("%s\\lib\\sql\\dbinit_%s.sql"),
513 pc->m_szInstallDir, szEngineName[pc->m_iDBEngine]);
514 bResult = ExecSQLBatch(hConn, szInitFile);
515
516 // Generate GUID for user "admin"
517 if (bResult)
518 {
519 uuid_generate(guid);
520 _sntprintf(szQuery, 256, _T("UPDATE users SET guid='%s' WHERE id=0"),
521 uuid_to_string(guid, szGUID));
522 bResult = DBQueryEx(hConn, szQuery);
523 }
524
525 // Generate GUID for "everyone" group
526 if (bResult)
527 {
528 uuid_generate(guid);
529 _sntprintf(szQuery, 256, _T("UPDATE user_groups SET guid='%s' WHERE id=%d"),
530 uuid_to_string(guid, szGUID), GROUP_EVERYONE);
531 bResult = DBQueryEx(hConn, szQuery);
532 }
533
534 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
535 }
536
537 // Set server parameters
538 if (bResult)
539 {
540 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Configuring server"));
541
542 #define __CHK(x) if (bResult) bResult = x;
543
544 __CHK(WriteConfigInt(hConn, _T("RunNetworkDiscovery"), pc->m_bRunAutoDiscovery, TRUE, TRUE));
545 __CHK(WriteConfigULong(hConn, _T("DiscoveryPollingInterval"), pc->m_dwDiscoveryPI, TRUE, TRUE));
546 __CHK(WriteConfigULong(hConn, _T("NumberOfStatusPollers"), pc->m_dwNumStatusPollers, TRUE, TRUE));
547 __CHK(WriteConfigULong(hConn, _T("StatusPollingInterval"), pc->m_dwStatusPI, TRUE, TRUE));
548 __CHK(WriteConfigULong(hConn, _T("NumberOfConfigurationPollers"), pc->m_dwNumConfigPollers, TRUE, TRUE));
549 __CHK(WriteConfigULong(hConn, _T("ConfigurationPollingInterval"), pc->m_dwConfigurationPI, TRUE, TRUE));
550
551 __CHK(WriteConfigStr(hConn, _T("SMTPServer"), pc->m_szSMTPServer, TRUE, FALSE));
552 __CHK(WriteConfigStr(hConn, _T("SMTPFromAddr"), pc->m_szSMTPMailFrom, TRUE, FALSE));
553
554 __CHK(WriteConfigInt(hConn, _T("EnableAdminInterface"), pc->m_bEnableAdminInterface, TRUE, TRUE));
555
556 _sntprintf(szDataDir, MAX_PATH, _T("%s\\var"), pc->m_szInstallDir);
557 __CHK(WriteConfigStr(hConn, _T("DataDirectory"), szDataDir, TRUE, TRUE));
558
559 __CHK(WriteConfigStr(hConn, _T("SMSDriver"), pc->m_szSMSDriver, TRUE, TRUE));
560 __CHK(WriteConfigStr(hConn, _T("SMSDrvConfig"), pc->m_szSMSDrvParam, TRUE, TRUE));
561
562 #undef __CHK
563
564 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
565 }
566
567 // Cleanup
568 if (hConn != NULL)
569 DBDisconnect(hConn);
570
571 // Install service
572 if (bResult)
573 {
574 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Installing service"));
575 bResult = InstallService(pc);
576 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
577 }
578
579 // Notify UI that job is finished
580 PostMessage(m_hStatusWnd, WM_JOB_FINISHED, bResult, 0);
581 return 0;
582 }
583
584
585 //
586 // Worker thread starter
587 //
588
589 void StartWizardWorkerThread(HWND hWnd, WIZARD_CFG_INFO *pc)
590 {
591 HANDLE hThread;
592 DWORD dwThreadId;
593
594 m_hStatusWnd = hWnd;
595 hThread = CreateThread(NULL, 0, WorkerThread, pc, 0, &dwThreadId);
596 if (hThread != NULL)
597 CloseHandle(hThread);
598 }