diff --git a/lib/agent/updater.js b/lib/agent/updater.js index 7b3569fb9..d4540745c 100644 --- a/lib/agent/updater.js +++ b/lib/agent/updater.js @@ -1,9 +1,15 @@ -var join = require('path').join, - exists = require('fs').exists, - common = require('./common'), - logger = common.logger.prefix('updater'), - system = common.system, - child_process = require('child_process'); // need to use child_process for stubbing to work in test +const { eq } = require('semver'); +var join = require('path').join, + exists = require('fs').exists, + common = require('./common'), + logger = common.logger.prefix('updater'), + system = common.system, + child_process = require('child_process'), + os = require('os'), + exec = child_process.exec, + needle = require('needle'), + updater_path = join(system.paths.package, 'bin', 'updater.exe'), + sys_win = require('./../system/windows/'); var timer, timer2; // for interval check exports.upgrading = false; @@ -32,6 +38,7 @@ var update_client = function(new_version, cb) { var bin_path = system.paths.package_bin; // /foo/bar/bin/prey var args = ['config', 'upgrade', new_version]; } + exports.upgrading = true; var let_the_child_go = function() { @@ -115,7 +122,50 @@ var update_client = function(new_version, cb) { }); } +/** + * Verify if winsvc must be updated + * @param {object} cb - a callback function + */ +exports.check_for_update_winsvc = (cb) => { + + /** Skip this block if OS is not windows. */ + if (os.platform() != 'win32') + return cb(new Error('Action only allowed on Windows')); + + /** Get the current version of winsvc running on the device. */ + sys_win.get_winsvc_version((err, current_service_version) => { + if (err) return cb(new Error("Error to get winsvc version")); + + if (!current_service_version) { + return cb(new Error("Error to get current winsvc version.")); + } + + /** Get the latest version of winsvc. */ + exports.get_stable_version_winsvc((err, service_version_stable) => { + if (err) return cb(new Error("Error to get stable version")); + + logger.notice('New version found winsvc: ' + service_version_stable); + + /** check if device is running the latest version. */ + if (service_version_stable && eq(current_service_version, service_version_stable)) { + logger.notice('Nothing to do. latest version already installed. ' + service_version_stable); + return cb(null, true) + } + + /** Perform the update. */ + exports.update_winsvc(updater_path + " -v=" + current_service_version, (err_update) => { + if (err_update) + return cb(new Error("error to update winsvc," + err_update.message)) + + return cb(null, true) + }) + }) + + }) +} + var check_for_update = function(cb) { + if (!exports.check_enabled || exports.upgrading) { if (cb && typeof cb == 'function') return cb(); else return; @@ -130,6 +180,7 @@ var check_for_update = function(cb) { common.package.new_version_available(branch, common.version, function(err, new_version) { if (err || !new_version) { common.package.check_update_success(common.version, versions_path, function(err) { + return cb && cb(err || new Error('Theres no new version available')); }) } else { @@ -137,6 +188,11 @@ var check_for_update = function(cb) { update_client(new_version, cb); } }) + + exports.check_for_update_winsvc((err,is_updated) => { + if(err) logger.info(err.message); + if(is_updated) logger.info("winsvc updated "); + }); } exports.check = function(id, target, opts, cb) { @@ -163,7 +219,9 @@ exports.check = function(id, target, opts, cb) { if (exports.upgrading) logger.warn('Already running upgrade process.') common.package.delete_attempts((err) => { - last_time = null; + if(err) + logger.error(err) + check_for_update((err) => { done(err); }); @@ -198,6 +256,16 @@ exports.check = function(id, target, opts, cb) { common.package.restart_client(); done(); break; + + case "update-winsvc": + // update winsvc + logger.info("command updating winsvc"); + exports.check_for_update_winsvc((err,is_updated) => { + if(err) logger.info(err.message); + if(is_updated) logger.info("winsvc updated from command"); + done(); + }); + break; default: logger.warn("Invalid target for upgrade command") @@ -210,7 +278,6 @@ exports.check_every = function(interval, cb) { if (!system.paths.versions) return cb && cb(no_versions_support_error()); - var interval = interval || 3 * 60 * 60 * 1000; // three hours by default timer = setInterval(() => { exports.check_enabled = true; exports.upgrading = false; @@ -220,10 +287,35 @@ exports.check_every = function(interval, cb) { exports.stop_checking = function() { if (timer) clearInterval(timer); - if (timer2) clearInterval(timer2); + if (timer2) clearInterval(timer2); timer = null; timer2 = null; } + exports.update_winsvc = (path,cb) => { + exec(path, (err, pid) => { + logger.info("executing service windows update!"+ path) + if (err) return cb(err) + return cb(null,pid) + }) +} + +exports.get_stable_version_winsvc = function(cb) { + let //releases_host = 'http://172.200.2.6', + //releases_url = releases_host + '/prey-releases/winsvc/', + releases_host = 'https://downloads.preyproject.com', + releases_url = releases_host + '/prey-client-releases/winsvc/', + latest_text = 'latest.txt'; + + let key = common.config.get('control-panel.device_key').toString() || null; + var options = { + headers: { 'resource-dk': key } + } + needle.get(releases_url + latest_text, key ? options : null, function(err, resp, body) { + var ver = body && body.toString().trim(); + cb(err, ver); + }); +} + exports.check_for_update = check_for_update; exports.logger = logger; \ No newline at end of file diff --git a/test/lib/agent/updating_winsvc_spec.js b/test/lib/agent/updating_winsvc_spec.js new file mode 100644 index 000000000..64f43f6b8 --- /dev/null +++ b/test/lib/agent/updating_winsvc_spec.js @@ -0,0 +1,223 @@ +/* eslint-disable no-undef */ +var join = require('path').join, + helpers = require(join('..', '..', 'helpers')), + should = require('should'), + sinon = require('sinon'), + assert = require('assert'), + sys_index_path = helpers.lib_path('system'), + sys_index = require(sys_index_path), + sys_win = require(join(sys_index_path, 'windows')), + os = require('os'), + sending = { count: 0 } + updater = require(helpers.lib_path('agent', 'updater')); + +function startCheckUpdateWinsvc(number) { + setInterval(function () { + sending.count += number; + updater.check_for_update_winsvc(() => { + }) + }, 100); +} + +describe('when os != windows', () => { + before(() => { + sys_index.os_name = "mac" + platform_stub = sinon.stub(os, 'platform').callsFake(() => { return 'mac'; }); + }) + + after(() => { + platform_stub.restore(); + }) + + it('returns an error', (done) => { + updater.check_for_update_winsvc((err) => { + should.exist(err); + err.message.should.containEql('Action only allowed on Windows'); + done(); + }); + }) +}) + +describe('update winsvcs interval', function () { + before(() => { + sys_index.os_name = "windows" + platform_stub_3 = sinon.stub(os, 'platform').callsFake(() => { return 'win32'; }); + }) + + describe('updater winsvc', function () { + + beforeEach(function () { + sys_index.os_name = "windows" + this.clock = sinon.useFakeTimers(); + }); + + afterEach(function () { + this.clock = sinon.restore(); + }); + + it('should be sent once', function () { + startCheckUpdateWinsvc(1); + //advance the clock + this.clock.tick(100); + this.clock.tick(100); + assert.equal(sending.count, 2); + }); + }) + +}) + +describe('when os is windows', () => { + before(() => { + sys_index.os_name = "windows" + sys_index.check_service = sys_win.check_service; + sys_index.run_as_admin = sys_win.run_as_admin; + platform_stub = sinon.stub(os, 'platform').callsFake(() => { return 'win32'; }); + }) + + after(() => { + platform_stub.restore(); + }) + + describe('when get_winsvc_version return error', () => { + + var get_winsvc_version_stub; + before(() => { + get_winsvc_version_stub = sinon.stub(sys_win, 'get_winsvc_version').callsFake((cb) => { + cb(new Error('No winsvc version found.')); + }); + }) + + after(() => { + get_winsvc_version_stub.restore(); + }) + + it('should return error to get os edition', (done) => { + updater.check_for_update_winsvc((err) => { + should.exist(err); + err.message.should.containEql('Error to get winsvc version'); + done(); + }); + }) + }) + + describe('when get_stable_version_winsvc return error', () => { + + var get_winsvc_version_2_1_0_stub; + var get_stable_version_winsvc_error; + before(() => { + get_winsvc_version_2_1_0_stub = sinon.stub(sys_win, 'get_winsvc_version').callsFake((cb) => { + cb(null, "2.0.1"); + }); + get_stable_version_winsvc_error = sinon.stub(updater, 'get_stable_version_winsvc').callsFake((cb) => { + cb(new Error('Error get_stable_version_winsvc.')); + }); + }) + + after(() => { + get_winsvc_version_2_1_0_stub.restore(); + get_stable_version_winsvc_error.restore(); + }) + + it('should return 2.0.1', (done) => { + updater.check_for_update_winsvc((err) => { + should.exist(err); + err.message.should.containEql('Error to get stable version'); + done(); + }); + }) + }) + + describe('when get_stable_version_winsvc return ok and is updated', () => { + + var get_winsvc_version_2_0_2_stub; + var get_stable_version_winsvc_ok; + before(() => { + get_winsvc_version_2_0_2_stub = sinon.stub(sys_win, 'get_winsvc_version').callsFake((cb) => { + cb(null, "2.0.2"); + }); + get_stable_version_winsvc_ok = sinon.stub(updater, 'get_stable_version_winsvc').callsFake((cb) => { + cb(null, "2.0.2"); + }); + + }) + + after(() => { + get_winsvc_version_2_0_2_stub.restore(); + get_stable_version_winsvc_ok.restore(); + }) + + it('should return 2.0.0', (done) => { + updater.check_for_update_winsvc((err) => { + should.not.exist(err); + done(); + }); + }) + }) + + describe('when get_stable_version_winsvc return ok and not updated and winsvc return error', () => { + + var get_winsvc_version_2_0_3_stub; + var get_stable_version_winsvc_ok; + var update_winsvc_error; + before(() => { + get_winsvc_version_2_0_3_stub = sinon.stub(sys_win, 'get_winsvc_version').callsFake((cb) => { + cb(null, "2.0.3"); + }); + get_stable_version_winsvc_ok = sinon.stub(updater, 'get_stable_version_winsvc').callsFake((cb) => { + cb(null, "2.0.4"); + }); + update_winsvc_error = sinon.stub(updater, 'update_winsvc').callsFake((path, cb) => { + cb(new Error('Error to update winsvc.')); + }); + }) + + after(() => { + get_winsvc_version_2_0_3_stub.restore(); + get_stable_version_winsvc_ok.restore(); + update_winsvc_error.restore(); + }) + + it('should return 2.0.4', (done) => { + updater.check_for_update_winsvc((err) => { + should.exist(err); + err.message.should.containEql('error to update winsvc'); + done(); + }); + }) + }) + + describe('when get_stable_version_winsvc return ok and not updated and winsvc update ok', () => { + + var get_winsvc_version_2_0_4_stub; + var get_stable_version_winsvc_ok; + var update_winsvc_error; + before(() => { + get_winsvc_version_2_0_4_stub = sinon.stub(sys_win, 'get_winsvc_version').callsFake((cb) => { + cb(null, "2.0.4"); + }); + get_stable_version_winsvc_ok = sinon.stub(updater, 'get_stable_version_winsvc').callsFake((cb) => { + cb(null, "2.0.5"); + }); + update_winsvc_error = sinon.stub(updater, 'update_winsvc').callsFake((path, cb) => { + cb(null, true); + }); + }) + + after(() => { + get_winsvc_version_2_0_4_stub.restore(); + get_stable_version_winsvc_ok.restore(); + update_winsvc_error.restore(); + }) + + it('should return 2.0.4', (done) => { + updater.check_for_update_winsvc((err, isUpdated) => { + should.not.exist(err); + should.exist(isUpdated); + console.log(isUpdated) + done(); + }); + }) + }) +}) + +