9d969a76348b9717ac48368c0f3cf4cc126d5b8f
[public/netxms.git] / src / server / tools / nxdbmgr / nxdbmgr.cpp
1 /*
2 ** nxdbmgr - NetXMS database manager
3 ** Copyright (C) 2004-2016 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: nxdbmgr.cpp
20 **
21 **/
22
23 #include "nxdbmgr.h"
24 #include <nxconfig.h>
25
26 #ifdef _WIN32
27 #include <conio.h>
28 #endif
29
30 /**
31 * Global variables
32 */
33 DB_HANDLE g_hCoreDB;
34 BOOL g_bIgnoreErrors = FALSE;
35 BOOL g_bTrace = FALSE;
36 bool g_isGuiMode = false;
37 bool g_checkData = false;
38 bool g_checkDataTablesOnly = false;
39 bool g_dataOnlyMigration = false;
40 bool g_skipDataMigration = false;
41 bool g_skipDataSchemaMigration = false;
42 int g_migrationTxnSize = 4096;
43 int g_dbSyntax;
44 const TCHAR *g_pszTableSuffix = _T("");
45 const TCHAR *g_pszSqlType[6][3] =
46 {
47 { _T("longtext"), _T("text"), _T("bigint") }, // MySQL
48 { _T("text"), _T("varchar(4000)"), _T("bigint") }, // PostgreSQL
49 { _T("text"), _T("varchar(4000)"), _T("bigint") }, // Microsoft SQL
50 { _T("clob"), _T("varchar(4000)"), _T("number(20)") }, // Oracle
51 { _T("varchar"), _T("varchar(4000)"), _T("number(20)") }, // SQLite
52 { _T("long varchar"), _T("varchar(4000)"), _T("bigint") } // DB/2
53 };
54
55 /**
56 * Static data
57 */
58 static char m_szCodePage[MAX_PATH] = ICONV_DEFAULT_CODEPAGE;
59 static TCHAR s_dbDriver[MAX_PATH] = _T("");
60 static TCHAR s_dbDrvParams[MAX_PATH] = _T("");
61 static TCHAR s_dbServer[MAX_PATH] = _T("127.0.0.1");
62 static TCHAR s_dbLogin[MAX_DB_LOGIN] = _T("netxms");
63 static TCHAR s_dbPassword[MAX_PASSWORD] = _T("");
64 static TCHAR s_dbName[MAX_DB_NAME] = _T("netxms_db");
65 static TCHAR s_dbSchema[MAX_DB_NAME] = _T("");
66 static NX_CFG_TEMPLATE m_cfgTemplate[] =
67 {
68 { _T("CodePage"), CT_MB_STRING, 0, 0, MAX_PATH, 0, m_szCodePage },
69 { _T("DBDriver"), CT_STRING, 0, 0, MAX_PATH, 0, s_dbDriver },
70 { _T("DBDrvParams"), CT_STRING, 0, 0, MAX_PATH, 0, s_dbDrvParams },
71 { _T("DBLogin"), CT_STRING, 0, 0, MAX_DB_LOGIN, 0, s_dbLogin },
72 { _T("DBName"), CT_STRING, 0, 0, MAX_DB_NAME, 0, s_dbName },
73 { _T("DBPassword"), CT_STRING, 0, 0, MAX_PASSWORD, 0, s_dbPassword },
74 { _T("DBEncryptedPassword"), CT_STRING, 0, 0, MAX_PASSWORD, 0, s_dbPassword },
75 { _T("DBSchema"), CT_STRING, 0, 0, MAX_DB_NAME, 0, s_dbSchema },
76 { _T("DBServer"), CT_STRING, 0, 0, MAX_PATH, 0, s_dbServer },
77 { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL }
78 };
79 static BOOL m_bForce = FALSE;
80 static DB_DRIVER s_driver = NULL;
81
82 /**
83 * Show query if trace mode is ON
84 */
85 void ShowQuery(const TCHAR *pszQuery)
86 {
87 WriteToTerminalEx(_T("\x1b[1m>>> \x1b[32;1m%s\x1b[0m\n"), pszQuery);
88 }
89
90 /**
91 * Get Yes or No answer from keyboard
92 */
93 bool GetYesNo(const TCHAR *format, ...)
94 {
95 va_list args;
96
97 if (g_isGuiMode)
98 {
99 if (m_bForce)
100 return true;
101
102 TCHAR message[4096];
103 va_start(args, format);
104 _vsntprintf(message, 4096, format, args);
105 va_end(args);
106
107 #ifdef _WIN32
108 return MessageBox(NULL, message, _T("NetXMS Database Manager"), MB_YESNO | MB_ICONQUESTION) == IDYES;
109 #else
110 return false;
111 #endif
112 }
113 else
114 {
115 va_start(args, format);
116 _vtprintf(format, args);
117 va_end(args);
118 _tprintf(_T(" (Y/N) "));
119
120 if (m_bForce)
121 {
122 _tprintf(_T("Y\n"));
123 return true;
124 }
125 else
126 {
127 #ifdef _WIN32
128 int ch;
129
130 while(1)
131 {
132 ch = _getch();
133 if ((ch == 'y') || (ch == 'Y'))
134 {
135 _tprintf(_T("Y\n"));
136 return true;
137 }
138 if ((ch == 'n') || (ch == 'N'))
139 {
140 _tprintf(_T("N\n"));
141 return false;
142 }
143 }
144 #else
145 TCHAR szBuffer[16];
146
147 fflush(stdout);
148 _fgetts(szBuffer, 16, stdin);
149 StrStrip(szBuffer);
150 return ((szBuffer[0] == 'y') || (szBuffer[0] == 'Y'));
151 #endif
152 }
153 }
154 }
155
156 /**
157 * Execute SQL SELECT query and print error message on screen if query failed
158 */
159 DB_RESULT SQLSelect(const TCHAR *pszQuery)
160 {
161 DB_RESULT hResult;
162 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
163
164 if (g_bTrace)
165 ShowQuery(pszQuery);
166
167 hResult = DBSelectEx(g_hCoreDB, pszQuery, errorText);
168 if (hResult == NULL)
169 WriteToTerminalEx(_T("SQL query failed (%s):\n\x1b[33;1m%s\x1b[0m\n"), errorText, pszQuery);
170 return hResult;
171 }
172
173 /**
174 * Execute SQL SELECT query via DBSelectUnbuffered and print error message on screen if query failed
175 */
176 DB_UNBUFFERED_RESULT SQLSelectUnbuffered(const TCHAR *pszQuery)
177 {
178 if (g_bTrace)
179 ShowQuery(pszQuery);
180
181 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
182 DB_UNBUFFERED_RESULT hResult = DBSelectUnbufferedEx(g_hCoreDB, pszQuery, errorText);
183 if (hResult == NULL)
184 WriteToTerminalEx(_T("SQL query failed (%s):\n\x1b[33;1m%s\x1b[0m\n"), errorText, pszQuery);
185 return hResult;
186 }
187
188 /**
189 * Execute prepared statement and print error message on screen if query failed
190 */
191 bool SQLExecute(DB_STATEMENT hStmt)
192 {
193 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
194
195 if (g_bTrace)
196 ShowQuery(DBGetStatementSource(hStmt));
197
198 bool result = DBExecuteEx(hStmt, errorText);
199 if (!result)
200 WriteToTerminalEx(_T("SQL query failed (%s):\n\x1b[33;1m%s\x1b[0m\n"), errorText, DBGetStatementSource(hStmt));
201 return result;
202 }
203
204 /**
205 * Execute SQL query and print error message on screen if query failed
206 */
207 bool SQLQuery(const TCHAR *pszQuery)
208 {
209 if (*pszQuery == 0)
210 return true;
211
212 String query(pszQuery);
213
214 query.replace(_T("$SQL:TEXT"), g_pszSqlType[g_dbSyntax][SQL_TYPE_TEXT]);
215 query.replace(_T("$SQL:TXT4K"), g_pszSqlType[g_dbSyntax][SQL_TYPE_TEXT4K]);
216 query.replace(_T("$SQL:INT64"), g_pszSqlType[g_dbSyntax][SQL_TYPE_INT64]);
217
218 if (g_bTrace)
219 ShowQuery(query);
220
221 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
222 bool success = DBQueryEx(g_hCoreDB, (const TCHAR *)query, errorText);
223 if (!success)
224 WriteToTerminalEx(_T("SQL query failed (%s):\n\x1b[33;1m%s\x1b[0m\n"), errorText, (const TCHAR *)query);
225 return success;
226 }
227
228 /**
229 * Execute SQL batch
230 */
231 bool SQLBatch(const TCHAR *pszBatch)
232 {
233 String batch(pszBatch);
234 TCHAR *pszBuffer, *pszQuery, *ptr;
235 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
236 bool success = true;
237 TCHAR table[128], column[128];
238
239 batch.replace(_T("$SQL:TEXT"), g_pszSqlType[g_dbSyntax][SQL_TYPE_TEXT]);
240 batch.replace(_T("$SQL:TXT4K"), g_pszSqlType[g_dbSyntax][SQL_TYPE_TEXT4K]);
241 batch.replace(_T("$SQL:INT64"), g_pszSqlType[g_dbSyntax][SQL_TYPE_INT64]);
242
243 pszQuery = pszBuffer = batch.getBuffer();
244 while(1)
245 {
246 ptr = _tcschr(pszQuery, _T('\n'));
247 if (ptr != NULL)
248 *ptr = 0;
249 if (!_tcscmp(pszQuery, _T("<END>")))
250 break;
251
252 if (_stscanf(pszQuery, _T("ALTER TABLE %128s DROP COLUMN %128s"), table, column) == 2)
253 {
254 if (!SQLDropColumn(table, column))
255 {
256 WriteToTerminalEx(_T("Cannot drop column \x1b[37;1m%s.%s\x1b[0m\n"), table, column);
257 if (!g_bIgnoreErrors)
258 {
259 success = false;
260 break;
261 }
262 }
263 }
264 else
265 {
266 if (g_bTrace)
267 ShowQuery(pszQuery);
268
269 if (!DBQueryEx(g_hCoreDB, pszQuery, errorText))
270 {
271 WriteToTerminalEx(_T("SQL query failed (%s):\n\x1b[33;1m%s\x1b[0m\n"), errorText, pszQuery);
272 if (!g_bIgnoreErrors)
273 {
274 success = false;
275 break;
276 }
277 }
278 }
279
280 ptr++;
281 pszQuery = ptr;
282 }
283 return success;
284 }
285
286 /**
287 * Drop column from the table
288 */
289 bool SQLDropColumn(const TCHAR *table, const TCHAR *column)
290 {
291 TCHAR query[1024];
292 DB_RESULT hResult;
293 bool success = false;
294
295 if (g_dbSyntax != DB_SYNTAX_SQLITE)
296 {
297 _sntprintf(query, 1024, _T("ALTER TABLE %s DROP COLUMN %s"), table, column);
298 success = SQLQuery(query);
299 if (g_dbSyntax == DB_SYNTAX_DB2)
300 {
301 _sntprintf(query, 1024, _T("CALL Sysproc.admin_cmd('REORG TABLE %s')"), table);
302 success = SQLQuery(query);
303 }
304 }
305 else
306 {
307 _sntprintf(query, 1024, _T("PRAGMA TABLE_INFO('%s')"), table);
308 hResult = SQLSelect(query);
309 if (hResult != NULL)
310 {
311 int rows = DBGetNumRows(hResult);
312 const int blen = 2048;
313 TCHAR buffer[blen];
314 // Intermediate buffers for SQLs
315 TCHAR columnList[1024], createList[1024];
316 // TABLE_INFO() columns
317 TCHAR tabColName[128], tabColType[64], tabColNull[10], tabColDefault[128];
318 columnList[0] = createList[0] = _T('\0');
319 for (int i = 0; i < rows; i++)
320 {
321 DBGetField(hResult, i, 1, tabColName, 128);
322 DBGetField(hResult, i, 2, tabColType, 64);
323 DBGetField(hResult, i, 3, tabColNull, 10);
324 DBGetField(hResult, i, 4, tabColDefault, 128);
325 if (_tcsnicmp(tabColName, column, 128))
326 {
327 _tcscat(columnList, tabColName);
328 if (columnList[0] != _T('\0'))
329 _tcscat(columnList, _T(","));
330 _tcscat(createList, tabColName);
331 _tcscat(createList, tabColType);
332 if (tabColDefault[0] != _T('\0'))
333 {
334 _tcscat(createList, _T("DEFAULT "));
335 _tcscat(createList, tabColDefault);
336 }
337 if (tabColNull[0] == _T('1'))
338 _tcscat(createList, _T(" NOT NULL"));
339 _tcscat(createList, _T(","));
340 }
341 }
342 DBFreeResult(hResult);
343 if (rows > 0)
344 {
345 int cllen = (int)_tcslen(columnList);
346 if (cllen > 0 && columnList[cllen - 1] == _T(','))
347 columnList[cllen - 1] = _T('\0');
348 // TODO: figure out if SQLite transactions will work here
349 _sntprintf(buffer, blen, _T("CREATE TABLE %s__backup__ (%s)"), table, columnList);
350 CHK_EXEC(SQLQuery(buffer));
351 _sntprintf(buffer, blen, _T("INSERT INTO %s__backup__ (%s) SELECT %s FROM %s"),
352 table, columnList, columnList, table);
353 CHK_EXEC(SQLQuery(buffer));
354 _sntprintf(buffer, blen, _T("DROP TABLE %s"), table);
355 CHK_EXEC(SQLQuery(buffer));
356 _sntprintf(buffer, blen, _T("ALTER TABLE %s__backup__ RENAME to %s"), table, table);
357 CHK_EXEC(SQLQuery(buffer));
358 success = true;
359 }
360 }
361 }
362
363 // TODO: preserve indices and constraints??
364
365 return success;
366 }
367
368 /**
369 * Read string value from metadata table
370 */
371 BOOL MetaDataReadStr(const TCHAR *pszVar, TCHAR *pszBuffer, int iBufSize, const TCHAR *pszDefault)
372 {
373 DB_RESULT hResult;
374 TCHAR szQuery[256];
375 BOOL bSuccess = FALSE;
376
377 nx_strncpy(pszBuffer, pszDefault, iBufSize);
378 if (_tcslen(pszVar) > 127)
379 return FALSE;
380
381 _sntprintf(szQuery, 256, _T("SELECT var_value FROM metadata WHERE var_name='%s'"), pszVar);
382 hResult = SQLSelect(szQuery);
383 if (hResult == NULL)
384 return FALSE;
385
386 if (DBGetNumRows(hResult) > 0)
387 {
388 DBGetField(hResult, 0, 0, pszBuffer, iBufSize);
389 bSuccess = TRUE;
390 }
391
392 DBFreeResult(hResult);
393 return bSuccess;
394 }
395
396 /**
397 * Read integer value from configuration table
398 */
399 int MetaDataReadInt(const TCHAR *pszVar, int iDefault)
400 {
401 TCHAR szBuffer[64];
402
403 if (MetaDataReadStr(pszVar, szBuffer, 64, _T("")))
404 return _tcstol(szBuffer, NULL, 0);
405 else
406 return iDefault;
407 }
408
409 /**
410 * Read string value from configuration table
411 */
412 BOOL ConfigReadStr(const TCHAR *pszVar, TCHAR *pszBuffer, int iBufSize, const TCHAR *pszDefault)
413 {
414 DB_RESULT hResult;
415 TCHAR szQuery[256];
416 BOOL bSuccess = FALSE;
417
418 nx_strncpy(pszBuffer, pszDefault, iBufSize);
419 if (_tcslen(pszVar) > 127)
420 return FALSE;
421
422 _sntprintf(szQuery, 256, _T("SELECT var_value FROM config WHERE var_name='%s'"), pszVar);
423 hResult = SQLSelect(szQuery);
424 if (hResult == NULL)
425 return FALSE;
426
427 if (DBGetNumRows(hResult) > 0)
428 {
429 DBGetField(hResult, 0, 0, pszBuffer, iBufSize);
430 bSuccess = TRUE;
431 }
432
433 DBFreeResult(hResult);
434 return bSuccess;
435 }
436
437 /**
438 * Read integer value from configuration table
439 */
440 int ConfigReadInt(const TCHAR *pszVar, int iDefault)
441 {
442 TCHAR szBuffer[64];
443
444 if (ConfigReadStr(pszVar, szBuffer, 64, _T("")))
445 return _tcstol(szBuffer, NULL, 0);
446 else
447 return iDefault;
448 }
449
450 /**
451 * Read unsigned long value from configuration table
452 */
453 DWORD ConfigReadULong(const TCHAR *pszVar, DWORD dwDefault)
454 {
455 TCHAR szBuffer[64];
456
457 if (ConfigReadStr(pszVar, szBuffer, 64, _T("")))
458 return _tcstoul(szBuffer, NULL, 0);
459 else
460 return dwDefault;
461 }
462
463 /**
464 * Check if given record exists in database
465 */
466 bool IsDatabaseRecordExist(const TCHAR *table, const TCHAR *idColumn, UINT32 id)
467 {
468 bool exist = false;
469
470 TCHAR query[256];
471 _sntprintf(query, 256, _T("SELECT %s FROM %s WHERE %s=?"), idColumn, table, idColumn);
472
473 DB_STATEMENT hStmt = DBPrepare(g_hCoreDB, query);
474 if (hStmt != NULL)
475 {
476 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, id);
477 DB_RESULT hResult = DBSelectPrepared(hStmt);
478 if (hResult != NULL)
479 {
480 exist = (DBGetNumRows(hResult) > 0);
481 DBFreeResult(hResult);
482 }
483 DBFreeStatement(hStmt);
484 }
485 return exist;
486 }
487
488 /**
489 * Check that database has correct schema version and is not locked
490 */
491 bool ValidateDatabase()
492 {
493 DB_RESULT hResult;
494 LONG nVersion = 0;
495 BOOL bLocked = FALSE;
496 TCHAR szLockStatus[MAX_DB_STRING], szLockInfo[MAX_DB_STRING];
497
498 // Get database format version
499 nVersion = DBGetSchemaVersion(g_hCoreDB);
500 if (nVersion < DB_FORMAT_VERSION)
501 {
502 _tprintf(_T("Your database has format version %d, this tool is compiled for version %d.\nUse \"upgrade\" command to upgrade your database first.\n"),
503 nVersion, DB_FORMAT_VERSION);
504 return false;
505 }
506 else if (nVersion > DB_FORMAT_VERSION)
507 {
508 _tprintf(_T("Your database has format version %d, this tool is compiled for version %d.\n")
509 _T("You need to upgrade your server before using this database.\n"),
510 nVersion, DB_FORMAT_VERSION);
511 return false;
512 }
513
514 // Check if database is locked
515 hResult = DBSelect(g_hCoreDB, _T("SELECT var_value FROM config WHERE var_name='DBLockStatus'"));
516 if (hResult != NULL)
517 {
518 if (DBGetNumRows(hResult) > 0)
519 {
520 DBGetField(hResult, 0, 0, szLockStatus, MAX_DB_STRING);
521 bLocked = _tcscmp(szLockStatus, _T("UNLOCKED"));
522 }
523 DBFreeResult(hResult);
524
525 if (bLocked)
526 {
527 hResult = DBSelect(g_hCoreDB, _T("SELECT var_value FROM config WHERE var_name='DBLockInfo'"));
528 if (hResult != NULL)
529 {
530 if (DBGetNumRows(hResult) > 0)
531 {
532 DBGetField(hResult, 0, 0, szLockInfo, MAX_DB_STRING);
533 }
534 DBFreeResult(hResult);
535 }
536 }
537 }
538
539 if (bLocked)
540 {
541 _tprintf(_T("Database is locked by server %s [%s]\n"), szLockStatus, szLockInfo);
542 return false;
543 }
544
545 return true;
546 }
547
548 /**
549 * Open database connection
550 */
551 DB_HANDLE ConnectToDatabase()
552 {
553 TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
554 DB_HANDLE hdb = DBConnect(s_driver, s_dbServer, s_dbName, s_dbLogin, s_dbPassword, s_dbSchema, errorText);
555 if (hdb == NULL)
556 {
557 _tprintf(_T("Unable to connect to database %s@%s as %s: %s\n"), s_dbName, s_dbServer, s_dbLogin, errorText);
558 }
559 return hdb;
560 }
561
562 /**
563 * Startup
564 */
565 int main(int argc, char *argv[])
566 {
567 BOOL bStart = TRUE, bQuiet = FALSE;
568 bool replaceValue = true;
569 int ch;
570
571 InitNetXMSProcess();
572
573 TCHAR configFile[MAX_PATH] = _T("");
574
575 // Try to read config location
576 #ifdef _WIN32
577 HKEY hKey;
578 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NetXMS\\Server"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
579 {
580 DWORD dwSize = MAX_PATH * sizeof(TCHAR);
581 RegQueryValueEx(hKey, _T("ConfigFile"), NULL, NULL, (BYTE *)configFile, &dwSize);
582 RegCloseKey(hKey);
583 }
584 #else
585 const TCHAR *env = _tgetenv(_T("NETXMSD_CONFIG"));
586 if ((env != NULL) && (*env != 0))
587 nx_strncpy(configFile, env, MAX_PATH);
588 #endif
589
590 // Search for config
591 if (configFile[0] == 0)
592 {
593 #ifdef _WIN32
594 TCHAR path[MAX_PATH];
595 GetNetXMSDirectory(nxDirEtc, path);
596 _tcscat(path, _T("\\netxmsd.conf"));
597 if (_taccess(path, 4) == 0)
598 {
599 _tcscpy(configFile, path);
600 }
601 else
602 {
603 _tcscpy(configFile, _T("C:\\netxmsd.conf"));
604 }
605 #else
606 const TCHAR *homeDir = _tgetenv(_T("NETXMS_HOME"));
607 if ((homeDir != NULL) && (*homeDir != 0))
608 {
609 TCHAR config[MAX_PATH];
610 _sntprintf(config, MAX_PATH, _T("%s/etc/netxmsd.conf"), homeDir);
611 if (_taccess(config, 4) == 0)
612 {
613 _tcscpy(configFile, config);
614 goto stop_search;
615 }
616 }
617 if (_taccess(PREFIX _T("/etc/netxmsd.conf"), 4) == 0)
618 {
619 _tcscpy(configFile, PREFIX _T("/etc/netxmsd.conf"));
620 }
621 else if (_taccess(_T("/usr/etc/netxmsd.conf"), 4) == 0)
622 {
623 _tcscpy(configFile, _T("/usr/etc/netxmsd.conf"));
624 }
625 else
626 {
627 _tcscpy(configFile, _T("/etc/netxmsd.conf"));
628 }
629 stop_search:
630 ;
631 #endif
632 }
633
634 // Parse command line
635 opterr = 1;
636 while((ch = getopt(argc, argv, "c:dDfGhIMNqsStT:vX")) != -1)
637 {
638 switch(ch)
639 {
640 case 'h': // Display help and exit
641 _tprintf(_T("NetXMS Database Manager Version ") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING _T(" (") NETXMS_BUILD_TAG _T(")") IS_UNICODE_BUILD_STRING _T("\n\n"));
642 _tprintf(_T("Usage: nxdbmgr [<options>] <command>\n")
643 _T("Valid commands are:\n")
644 _T(" batch <file> : Run SQL batch file\n")
645 _T(" check : Check database for errors\n")
646 _T(" check-data-tables : Check database for missing data tables\n")
647 _T(" export <file> : Export database to file\n")
648 _T(" get <name> : Get value of server configuration variable\n")
649 _T(" import <file> : Import database from file\n")
650 _T(" init <file> : Initialize database\n")
651 _T(" migrate <source> : Migrate database from given source\n")
652 _T(" reset-system-account : Unlock user \"system\" and reset it's password to default\n")
653 _T(" set <name> <value> : Set value of server configuration variable\n")
654 _T(" unlock : Forced database unlock\n")
655 _T(" upgrade : Upgrade database to new version\n")
656 _T("Valid options are:\n")
657 _T(" -c <config> : Use alternate configuration file. Default is %s\n")
658 _T(" -d : Check collected data (may take very long time).\n")
659 _T(" -D : Migrate only collected data.\n")
660 _T(" -f : Force repair - do not ask for confirmation.\n")
661 #ifdef _WIN32
662 _T(" -G : GUI mode.\n")
663 #endif
664 _T(" -h : Display help and exit.\n")
665 _T(" -I : MySQL only - specify TYPE=InnoDB for new tables.\n")
666 _T(" -M : MySQL only - specify TYPE=MyISAM for new tables.\n")
667 _T(" -N : Do not replace existing configuration value (\"set\" command only).\n")
668 _T(" -q : Quiet mode (don't show startup banner).\n")
669 _T(" -s : Skip collected data during migration or export.\n")
670 _T(" -S : Skip collected data during migration or export and do not clear or create data tables.\n")
671 _T(" -t : Enable trace mode (show executed SQL queries).\n")
672 _T(" -T <recs> : Transaction size for migration.\n")
673 _T(" -v : Display version and exit.\n")
674 _T(" -X : Ignore SQL errors when upgrading (USE WITH CAUTION!!!)\n")
675 _T("\n"), configFile);
676 bStart = FALSE;
677 break;
678 case 'v': // Print version and exit
679 _tprintf(_T("NetXMS Database Manager Version ") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING _T(" (") NETXMS_BUILD_TAG _T(")") IS_UNICODE_BUILD_STRING _T("\n\n"));
680 bStart = FALSE;
681 break;
682 case 'c':
683 #ifdef UNICODE
684 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, configFile, MAX_PATH);
685 configFile[MAX_PATH - 1] = 0;
686 #else
687 nx_strncpy(configFile, optarg, MAX_PATH);
688 #endif
689 break;
690 case 'd':
691 g_checkData = true;
692 break;
693 case 'D':
694 g_dataOnlyMigration = true;
695 break;
696 case 'f':
697 m_bForce = TRUE;
698 break;
699 case 'G':
700 g_isGuiMode = true;
701 break;
702 case 'N':
703 replaceValue = false;
704 break;
705 case 'q':
706 bQuiet = TRUE;
707 break;
708 case 's':
709 g_skipDataMigration = true;
710 break;
711 case 'S':
712 g_skipDataMigration = true;
713 g_skipDataSchemaMigration = true;
714 break;
715 case 't':
716 g_bTrace = TRUE;
717 break;
718 case 'T':
719 g_migrationTxnSize = strtol(optarg, NULL, 0);
720 if ((g_migrationTxnSize < 1) || (g_migrationTxnSize > 100000))
721 {
722 _tprintf(_T("WARNING: invalid transaction size, reset to default"));
723 g_migrationTxnSize = 4096;
724 }
725 break;
726 case 'I':
727 g_pszTableSuffix = _T(" TYPE=InnoDB");
728 break;
729 case 'M':
730 g_pszTableSuffix = _T(" TYPE=MyISAM");
731 break;
732 case 'X':
733 g_bIgnoreErrors = TRUE;
734 break;
735 case '?':
736 bStart = FALSE;
737 break;
738 default:
739 break;
740 }
741 }
742
743 if (!bStart)
744 return 1;
745
746 if (!bQuiet)
747 _tprintf(_T("NetXMS Database Manager Version ") NETXMS_VERSION_STRING _T(" Build ") NETXMS_VERSION_BUILD_STRING _T(" (") NETXMS_BUILD_TAG _T(")") IS_UNICODE_BUILD_STRING _T("\n\n"));
748
749 // Check parameter correctness
750 if (argc - optind == 0)
751 {
752 _tprintf(_T("Command missing. Type nxdbmgr -h for command line syntax.\n"));
753 return 1;
754 }
755 if (strcmp(argv[optind], "batch") &&
756 strcmp(argv[optind], "check") &&
757 strcmp(argv[optind], "check-data-tables") &&
758 strcmp(argv[optind], "export") &&
759 strcmp(argv[optind], "get") &&
760 strcmp(argv[optind], "import") &&
761 strcmp(argv[optind], "init") &&
762 strcmp(argv[optind], "migrate") &&
763 strcmp(argv[optind], "reset-system-account") &&
764 strcmp(argv[optind], "set") &&
765 strcmp(argv[optind], "unlock") &&
766 strcmp(argv[optind], "upgrade"))
767 {
768 _tprintf(_T("Invalid command \"%hs\". Type nxdbmgr -h for command line syntax.\n"), argv[optind]);
769 return 1;
770 }
771 if (((!strcmp(argv[optind], "init") || !strcmp(argv[optind], "batch") || !strcmp(argv[optind], "export") || !strcmp(argv[optind], "import") || !strcmp(argv[optind], "get") || !strcmp(argv[optind], "migrate")) && (argc - optind < 2)) ||
772 (!strcmp(argv[optind], "set") && (argc - optind < 3)))
773 {
774 _tprintf(_T("Required command argument(s) missing\n"));
775 return 1;
776 }
777
778 // Read configuration file
779 Config *config = new Config();
780 if (!config->loadIniConfig(configFile, _T("server")) || !config->parseTemplate(_T("server"), m_cfgTemplate))
781 {
782 _tprintf(_T("Error loading configuration file\n"));
783 return 2;
784 }
785 delete config;
786
787 // Decrypt password
788 DecryptPassword(s_dbLogin, s_dbPassword, s_dbPassword, MAX_PASSWORD);
789
790 #ifndef _WIN32
791 SetDefaultCodepage(m_szCodePage);
792 #endif
793
794 // Connect to database
795 if (!DBInit(0, 0))
796 {
797 _tprintf(_T("Unable to initialize database library\n"));
798 return 3;
799 }
800
801 s_driver = DBLoadDriver(s_dbDriver, s_dbDrvParams, false, NULL, NULL);
802 if (s_driver == NULL)
803 {
804 _tprintf(_T("Unable to load and initialize database driver \"%s\"\n"), s_dbDriver);
805 return 3;
806 }
807
808 g_hCoreDB = ConnectToDatabase();
809 if (g_hCoreDB == NULL)
810 {
811 DBUnloadDriver(s_driver);
812 return 4;
813 }
814
815 if (!strcmp(argv[optind], "init"))
816 {
817 InitDatabase(argv[optind + 1]);
818 }
819 else
820 {
821 // Get database syntax
822 g_dbSyntax = DBGetSyntax(g_hCoreDB);
823 if (g_dbSyntax == DB_SYNTAX_UNKNOWN)
824 {
825 _tprintf(_T("Unable to determine database syntax\n"));
826 DBDisconnect(g_hCoreDB);
827 DBUnloadDriver(s_driver);
828 return 5;
829 }
830
831 // Do requested operation
832 if (!strcmp(argv[optind], "batch"))
833 {
834 ExecSQLBatch(argv[optind + 1]);
835 }
836 else if (!strcmp(argv[optind], "check"))
837 {
838 CheckDatabase();
839 }
840 else if (!strcmp(argv[optind], "check-data-tables"))
841 {
842 g_checkDataTablesOnly = true;
843 CheckDatabase();
844 }
845 else if (!strcmp(argv[optind], "upgrade"))
846 {
847 UpgradeDatabase();
848 }
849 else if (!strcmp(argv[optind], "unlock"))
850 {
851 UnlockDatabase();
852 }
853 else if (!strcmp(argv[optind], "export"))
854 {
855 ExportDatabase(argv[optind + 1]);
856 }
857 else if (!strcmp(argv[optind], "import"))
858 {
859 ImportDatabase(argv[optind + 1]);
860 }
861 else if (!strcmp(argv[optind], "migrate"))
862 {
863 #ifdef UNICODE
864 WCHAR *sourceConfig = WideStringFromMBString(argv[optind + 1]);
865 #else
866 char *sourceConfig = argv[optind + 1];
867 #endif
868 MigrateDatabase(sourceConfig);
869 #ifdef UNICODE
870 free(sourceConfig);
871 #endif
872 }
873 else if (!strcmp(argv[optind], "get"))
874 {
875 #ifdef UNICODE
876 WCHAR *var = WideStringFromMBString(argv[optind + 1]);
877 #else
878 char *var = argv[optind + 1];
879 #endif
880 TCHAR buffer[MAX_CONFIG_VALUE];
881 ConfigReadStr(var, buffer, MAX_CONFIG_VALUE, _T(""));
882 _tprintf(_T("%s\n"), buffer);
883 #ifdef UNICODE
884 free(var);
885 #endif
886 }
887 else if (!strcmp(argv[optind], "set"))
888 {
889 #ifdef UNICODE
890 WCHAR *var = WideStringFromMBString(argv[optind + 1]);
891 WCHAR *value = WideStringFromMBString(argv[optind + 2]);
892 #else
893 char *var = argv[optind + 1];
894 char *value = argv[optind + 2];
895 #endif
896 CreateConfigParam(var, value, true, false, replaceValue);
897 #ifdef UNICODE
898 free(var);
899 free(value);
900 #endif
901 }
902 else if (!strcmp(argv[optind], "reset-system-account"))
903 {
904 ResetSystemAccount();
905 }
906 }
907
908 // Shutdown
909 DBDisconnect(g_hCoreDB);
910 DBUnloadDriver(s_driver);
911 return 0;
912 }