From b0579baa8555ade82be7ba6a11ebbac82b4c0c64 Mon Sep 17 00:00:00 2001 From: Noah Zalev Date: Sat, 10 Apr 2021 14:23:07 -0400 Subject: [PATCH 1/6] mongodump to dev null for dry-run effect --- db.js | 1 + 1 file changed, 1 insertion(+) diff --git a/db.js b/db.js index bf92e090..1062e287 100644 --- a/db.js +++ b/db.js @@ -1595,6 +1595,7 @@ module.exports.CreateDB = function (parent, func) { if (parent.config.settings.autobackup && parent.config.settings.autobackup.mongodumppath) { mongoDumpPath = parent.config.settings.autobackup.mongodumppath; } var cmd = '"' + mongoDumpPath + '"'; if (dburl) { cmd = '\"' + mongoDumpPath + '\" --uri=\"' + dburl.replace('?', '/?') + '\"'; } + cmd += (parent.platform == 'win32') ? ' --archive=\"nul\"' : ' --archive=\"/dev/null\"'; const child_process = require('child_process'); child_process.exec(cmd, { cwd: backupPath }, function (error, stdout, stderr) { try { From 34aa47a81330764b3fa9aec16b55e5e3e5f14716 Mon Sep 17 00:00:00 2001 From: Noah Zalev Date: Sat, 10 Apr 2021 17:21:02 -0400 Subject: [PATCH 2/6] Added MariaDB/MySQL to checkBackupCapability --- db.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/db.js b/db.js index 1062e287..aedc0925 100644 --- a/db.js +++ b/db.js @@ -1610,6 +1610,32 @@ module.exports.CreateDB = function (parent, func) { } } catch (ex) { console.log(ex); } }); + } else if ((obj.databaseType == 4) || (obj.databaseType == 5)) { + // Check that we have access to mysqldump + var backupPath = parent.backuppath; + if (parent.config.settings.autobackup && parent.config.settings.autobackup.backuppath) { backupPath = parent.config.settings.autobackup.backuppath; } + try { parent.fs.mkdirSync(backupPath); } catch (e) { } + var props = (obj.databaseType == 4) ? parent.args.mariadb : parent.args.mysql; + var mysqldumpPath = 'mysqldump'; + if (parent.config.settings.autobackup && parent.config.settings.autobackup.mysqldumppath) { mysqldumpPath = parent.config.settings.autobackup.mysqldumppath; } + var cmd = '\"' + mysqldumpPath + '\" --user=\'' + props.user + '\' --password=\'' + props.password + '\''; + if (props.host) { cmd += ' -h ' + props.host; } + if (props.port) { cmd += ' -P ' + props.port; } + cmd += ' meshcentral --result-file=' + ((parent.platform == 'win32') ? '\"nul\"' : '\"/dev/null\"'); + const child_process = require('child_process'); + child_process.exec(cmd, { cwd: backupPath }, function(error, stdout, stdin) { + try { + if ((error != null) && (error != '')) { + if (parent.platform == 'win32') { + func(1, "Unable to find mysqldump.exe, MySQL/MariaDB database auto-backup will not be performed."); + } else { + func(1, "Unable to find mysqldump, MySQL/MariaDB database auto-backup will not be performed."); + } + } else { + func(); + } + } catch (ex) { console.log(ex); } + }); } else { func(); } From 4b9cf142e1282ca02ecc1c53399cd2e171b254c2 Mon Sep 17 00:00:00 2001 From: Noah Zalev Date: Sat, 10 Apr 2021 18:18:27 -0400 Subject: [PATCH 3/6] Added mysqldump to performBackup --- db.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/db.js b/db.js index aedc0925..c3506a56 100644 --- a/db.js +++ b/db.js @@ -1808,6 +1808,48 @@ module.exports.CreateDB = function (parent, func) { archive.finalize(); } catch (ex) { console.log(ex); } }); + } else if ((obj.databaseType == 4) || (obj.databaseType == 5)) { + // Perform a MySqlDump backup + const newBackupFile = 'mysqldump-' + fileSuffix; + var newBackupPath = parent.path.join(backupPath, newBackupFile); + var props = (obj.databaseType == 4) ? parent.args.mariadb : parent.args.mysql; + var mysqldumpPath = 'mysqldump'; + if (parent.config.settings.autobackup && parent.config.settings.autobackup.mysqldumppath) { mysqldumpPath = parent.config.settings.autobackup.mysqldumppath; } + var cmd = '\"' + mysqldumpPath + '\" --user=\'' + props.user + '\' --password=\'' + props.password + '\''; + if (props.host) { cmd += ' -h ' + props.host; } + if (props.port) { cmd += ' -P ' + props.port; } + cmd += ' meshcentral --result-file=\"' + newBackupPath + '.sql\"'; + const child_process = require('child_process'); + var backupProcess = child_process.exec(cmd, { cwd: backupPath }, function (error, stdout, stderr) { + try { + var sqlDumpSuccess = true; + backupProcess = null; + if ((error != null) && (error != '')) { sqlDumpSuccess = false; console.log('ERROR: Unable to perform MySQL/MariaDB backup: ' + error + '\r\n'); } + + var archiver = require('archiver'); + var output = parent.fs.createWriteStream(newAutoBackupPath + '.zip'); + var archive = null; + if (parent.config.settings.autobackup && (typeof parent.config.settings.autobackup.zippassword == 'string')) { + try { archiver.registerFormat('zip-encrypted', require("archiver-zip-encrypted")); } catch (ex) { } + archive = archiver.create('zip-encrypted', { zlib: { level: 9 }, encryptionMethod: 'aes256', password: parent.config.settings.autobackup.zippassword }); + } else { + archive = archiver('zip', { zlib: { level: 9 } }); + } + output.on('close', function () { + obj.performingBackup = false; + if (func) { if (sqlDumpSuccess) { func('Auto-backup completed.'); } else { func('Auto-backup completed without mongodb database: ' + error); } } + obj.performCloudBackup(newAutoBackupPath + '.zip', func); + setTimeout(function () { try { parent.fs.unlink(newBackupPath + '.sql', function () { }); } catch (ex) { console.log(ex); } }, 5000); + }); + output.on('end', function () { }); + archive.on('warning', function (err) { console.log('Backup warning: ' + err); if (func) { func('Backup warning: ' + err); } }); + archive.on('error', function (err) { console.log('Backup error: ' + err); if (func) { func('Backup error: ' + err); } }); + archive.pipe(output); + if (sqlDumpSuccess == true) { archive.file(newBackupPath + '.sql', { name: newBackupFile + '.sql' }); } + archive.directory(parent.datapath, 'meshcentral-data'); + archive.finalize(); + } catch (ex) { console.log(ex); } + }); } else { // Perform a NeDB backup var archiver = require('archiver'); From 926d454cfd71c13282e0b45b439b77f747264d32 Mon Sep 17 00:00:00 2001 From: Noah Zalev Date: Sat, 10 Apr 2021 18:21:44 -0400 Subject: [PATCH 4/6] Added mysqldump path to config schema --- meshcentral-config-schema.json | 1 + 1 file changed, 1 insertion(+) diff --git a/meshcentral-config-schema.json b/meshcentral-config-schema.json index 329689a0..6267e9c5 100644 --- a/meshcentral-config-schema.json +++ b/meshcentral-config-schema.json @@ -141,6 +141,7 @@ "type": "object", "properties": { "mongoDumpPath": { "type": "string" }, + "mysqlDumpPath": { "type": "string"}, "backupIntervalHours": { "type": "integer" }, "keepLastDaysBackup": { "type": "integer" }, "zipPassword": { "type": "string" }, From 72afaaa908f732dc853098990742fad586e6ce4f Mon Sep 17 00:00:00 2001 From: Noah Zalev Date: Sat, 10 Apr 2021 20:11:17 -0400 Subject: [PATCH 5/6] Fixed sqldump pw escaping on windows --- db.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/db.js b/db.js index c3506a56..ee5a7606 100644 --- a/db.js +++ b/db.js @@ -1618,10 +1618,12 @@ module.exports.CreateDB = function (parent, func) { var props = (obj.databaseType == 4) ? parent.args.mariadb : parent.args.mysql; var mysqldumpPath = 'mysqldump'; if (parent.config.settings.autobackup && parent.config.settings.autobackup.mysqldumppath) { mysqldumpPath = parent.config.settings.autobackup.mysqldumppath; } - var cmd = '\"' + mysqldumpPath + '\" --user=\'' + props.user + '\' --password=\'' + props.password + '\''; + var cmd = '\"' + mysqldumpPath + '\" --user=\'' + props.user + '\''; + // Windows will treat ' as part of the pw. Linux/Unix requires it to escape. + cmd += (parent.platform == 'win32') ? ' --password=\"' + props.password + '\"' : ' --password=\'' + props.password + '\''; if (props.host) { cmd += ' -h ' + props.host; } if (props.port) { cmd += ' -P ' + props.port; } - cmd += ' meshcentral --result-file=' + ((parent.platform == 'win32') ? '\"nul\"' : '\"/dev/null\"'); + cmd += ' meshcentral > ' + ((parent.platform == 'win32') ? '\"nul\"' : '\"/dev/null\"'); const child_process = require('child_process'); child_process.exec(cmd, { cwd: backupPath }, function(error, stdout, stdin) { try { @@ -1815,7 +1817,9 @@ module.exports.CreateDB = function (parent, func) { var props = (obj.databaseType == 4) ? parent.args.mariadb : parent.args.mysql; var mysqldumpPath = 'mysqldump'; if (parent.config.settings.autobackup && parent.config.settings.autobackup.mysqldumppath) { mysqldumpPath = parent.config.settings.autobackup.mysqldumppath; } - var cmd = '\"' + mysqldumpPath + '\" --user=\'' + props.user + '\' --password=\'' + props.password + '\''; + var cmd = '\"' + mysqldumpPath + '\" --user=\'' + props.user + '\''; + // Windows will treat ' as part of the pw. Linux/Unix requires it to escape. + cmd += (parent.platform == 'win32') ? ' --password=\"' + props.password + '\"' : ' --password=\'' + props.password + '\''; if (props.host) { cmd += ' -h ' + props.host; } if (props.port) { cmd += ' -P ' + props.port; } cmd += ' meshcentral --result-file=\"' + newBackupPath + '.sql\"'; From e79b2b807084f2374e7c44cccaf162cb4f195b34 Mon Sep 17 00:00:00 2001 From: Noah Zalev Date: Sat, 10 Apr 2021 20:41:50 -0400 Subject: [PATCH 6/6] Update db.js Updated failure string --- db.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db.js b/db.js index ee5a7606..d2d318be 100644 --- a/db.js +++ b/db.js @@ -1841,7 +1841,7 @@ module.exports.CreateDB = function (parent, func) { } output.on('close', function () { obj.performingBackup = false; - if (func) { if (sqlDumpSuccess) { func('Auto-backup completed.'); } else { func('Auto-backup completed without mongodb database: ' + error); } } + if (func) { if (sqlDumpSuccess) { func('Auto-backup completed.'); } else { func('Auto-backup completed without MySQL/MariaDB database: ' + error); } } obj.performCloudBackup(newAutoBackupPath + '.zip', func); setTimeout(function () { try { parent.fs.unlink(newBackupPath + '.sql', function () { }); } catch (ex) { console.log(ex); } }, 5000); });