diff --git a/lib/agent/actions.js b/lib/agent/actions.js index d92b488ba..d364c0ef3 100644 --- a/lib/agent/actions.js +++ b/lib/agent/actions.js @@ -116,7 +116,6 @@ actions.stop = function(name) { } else { action.stop(); } - } actions.stop_all = function() { diff --git a/lib/agent/actions/alarm/lib/alarm.mp3 b/lib/agent/actions/alarm/lib/alarm.mp3 index 8a3bf0016..843468c59 100644 Binary files a/lib/agent/actions/alarm/lib/alarm.mp3 and b/lib/agent/actions/alarm/lib/alarm.mp3 differ diff --git a/lib/agent/cli.js b/lib/agent/cli.js index 4c2b455c3..974d784a6 100644 --- a/lib/agent/cli.js +++ b/lib/agent/cli.js @@ -37,24 +37,19 @@ var common = require('./common'), logger = common.logger, pidfile; // null unless we do set one. -var write = function(str) { +function warn(str, code) { if (process.stdout.writable) process.stdout.write(str + '\n'); -} -if (program.nodeVersion) { - return logger.write(process.version); -} else if (!common.config.present()) { - write('\nLooks like there\'s no config file yet. Glad to see you\'re getting started. :)'); - write('To finish setting up Prey, please run `prey config hooks post_install` as root.\n'); - return process.exit(1); + if (typeof code != 'undefined') + process.exit(code); } ///////////////////////////////////////////////////////////// // event, signal handlers ///////////////////////////////////////////////////////////// -var shutdown = function(code, wait) { +function shutdown(code, wait) { var wait = wait || 10000; var die = function(graceful) { @@ -79,91 +74,107 @@ var shutdown = function(code, wait) { } } -process.on('exit', function(code) { - shutdown(code); -}); - -// sent by other instance when updating config -// SIGHUP is the default signal sent by Upstart when reloading a job. -process.on('SIGHUP', function() { - logger.warn('Got SIGHUP signal!'); - agent.reload(); -}) - -// sent by Upstart -process.on('SIGQUIT', function() { - logger.warn('Got QUIT signal.'); - shutdown(0, 10000); -}); - -// sent by LaunchDaemon -// we cannot exit with code 0 as LaunchDaemon -// will assume the process exited normally. -process.on('SIGTERM', function() { - logger.warn('Got TERM signal.'); - shutdown(11, 10000); -}); - -// sent when developing. :) -// 130 is the 'official' exit code in Bash for SIGINTs -process.on('SIGINT', function() { - if (!agent.running()) { - logger.warn('Ok, ok, whatever you say.'); - return process.exit(2); - } +function trap_signals() { + + process.on('exit', function(code) { + shutdown(code); + }); + + // sent by other instance when updating config + // SIGHUP is the default signal sent by Upstart when reloading a job. + process.on('SIGHUP', function() { + logger.warn('Got SIGHUP signal!'); + agent.reload(); + }) + + // sent by Upstart + process.on('SIGQUIT', function() { + logger.warn('Got QUIT signal.'); + shutdown(0, 10000); + }); + + // sent by LaunchDaemon + // we cannot exit with code 0 as LaunchDaemon + // will assume the process exited normally. + process.on('SIGTERM', function() { + logger.warn('Got TERM signal.'); + shutdown(11, 10000); + }); - logger.warn('Got INT signal.'); - shutdown(130, 5000); -}); + // sent when developing. :) + // 130 is the 'official' exit code in Bash for SIGINTs + process.on('SIGINT', function() { + if (!agent.running()) { + logger.warn('Ok, ok, whatever you say.'); + return process.exit(2); + } + + logger.warn('Got INT signal.'); + shutdown(130, 5000); + }); -process.on('uncaughtException', function (err) { - logger.critical('UNCAUGHT EXCEPTION: ' + (err.message || err)); - logger.debug(err.stack); + process.on('uncaughtException', function (err) { + logger.critical('UNCAUGHT EXCEPTION: ' + (err.message || err)); + logger.debug(err.stack); - if (!common.config.get('send_crash_reports')) - return shutdown(1, 5000); + if (!common.config.get('send_crash_reports')) + return shutdown(1, 5000); - common.exceptions.send(err, function() { - shutdown(1, 5000); + common.exceptions.send(err, function() { + shutdown(1, 5000); + }); }); -}); + +} //////////////////////////////////////////////////////////// // launcher ///////////////////////////////////////////////////////////// -if (process.argv[2] == 'console') - program.mode = 'console'; +(function() { -if (program.allowOther || program.run || program.mode == 'console') { - return agent.run(); -} + if (program.nodeVersion) { + return warn(process.version, 0); + } else if (!common.config.present()) { + warn('\nLooks like there\'s no config file yet. Glad to see you\'re getting started. :)'); + return warn('To finish setting up Prey, please run `prey config hooks post_install` as root.\n', 1); + } + + trap_signals(); -// if running in console mode, or using -a or -r, pidfile won't -// be available for them to remove on process.exit(). -pidfile = common.pid_file; + if (process.argv[2] == 'console') + program.mode = 'console'; -pid.store(pidfile, function(err, running) { - if (err) { - var msg = err.code == 'EPERM' ? 'No write access to pidfile: ' + pidfile : err.message; - write('Cannot continue: ' + msg); - return process.exit(1); + if (program.allowOther || program.run || program.mode == 'console') { + return agent.run(); } - if (!running) return agent.run(); + // if running in console mode, or using -a or -r, pidfile won't + // be available for them to remove on process.exit(). + pidfile = common.pid_file; - if (process.stdout.writable) { - write('\n The Prey agent is running. Good job!'); + pid.store(pidfile, function(err, running) { + if (err) { + var msg = err.code == 'EPERM' ? 'No write access to pidfile: ' + pidfile : err.message; + return warn('Cannot continue: ' + msg, 1); + } + + if (!running) return agent.run(); + + if (process.stdout.writable) { + warn('\n The Prey agent is running. Good job!'); - if (running.stat && running.stat.ctime) { - var run_time = (new Date() - running.stat.ctime)/(60 * 1000); - var run_time_str = run_time.toString().substring(0,5); - write(' It has been live for ' + run_time_str + ' minutes, under process ID ' + running.pid + '.'); + if (running.stat && running.stat.ctime) { + var run_time = (new Date() - running.stat.ctime)/(60 * 1000); + var run_time_str = run_time.toString().substring(0,5); + warn(' It has been live for ' + run_time_str + ' minutes, under process ID ' + running.pid + '.'); + } + + warn(' To trigger actions or retrieve information, log in to your Prey account at https://preyproject.com'); + warn(' For additional configuration options, please run `prey config`.\n'); } - write(' To trigger actions or retrieve information, log in to your Prey account at https://preyproject.com'); - write(' For additional configuration options, please run `prey config`.\n'); - } + process.exit(10); + }); - process.exit(10); -}); +})(); diff --git a/lib/agent/hooks.js b/lib/agent/hooks.js index 9f3ac0948..7cc6657d6 100644 --- a/lib/agent/hooks.js +++ b/lib/agent/hooks.js @@ -1,6 +1,5 @@ var logger = require('./common').logger.prefix('hooks'), Emitter = require('events').EventEmitter, - hooks = {}, emitter = new Emitter(); var trigger = function(event) { diff --git a/lib/agent/plugins/control-panel/api/accounts.js b/lib/agent/plugins/control-panel/api/accounts.js index fd3889430..7c1313b1a 100644 --- a/lib/agent/plugins/control-panel/api/accounts.js +++ b/lib/agent/plugins/control-panel/api/accounts.js @@ -33,10 +33,10 @@ exports.authorize = function(opts, cb) { if (resp.statusCode == 401) { cb(errors.get('INVALID_CREDENTIALS')) - } else if (body.user && parseInt(body.user.available_slots) <= 0) { + } else if (body && body.user && parseInt(body.user.available_slots) <= 0) { cb(errors.get('NO_AVAILABLE_SLOTS')); - } else if (body && body.key || (body.user && body.user.key)) { + } else if (body && body.key || (body && body.user && body.user.key)) { cb(null, set(body.key || body.user.key)); } else { diff --git a/lib/agent/plugins/control-panel/index.js b/lib/agent/plugins/control-panel/index.js index 83d788ba0..004c51915 100644 --- a/lib/agent/plugins/control-panel/index.js +++ b/lib/agent/plugins/control-panel/index.js @@ -40,18 +40,20 @@ var wait_for_config = function() { var attempts = 0; var timer = setInterval(function() { + logger.info('Reloading config...'); config.reload(); if (config.get('api_key') && config.get('device_key')) { + clearInterval(timer); // set the new keys in the api before booting - init_api(config.all(), function() { - boot(); - }) + init_api(config.all(), function() { boot() }); + } else if (++attempts > 30) { // five mins total throw new Error('Not configured. Stopping.'); } + }, 10000); // 10 seconds } @@ -250,7 +252,7 @@ exports.unload = function(cb) { } // export API for conf module -exports.load_api = function(opts) { - init_api(opts); +exports.load_api = function(opts, cb) { + init_api(opts, cb); return api; }; diff --git a/lib/agent/plugins/control-panel/sender.js b/lib/agent/plugins/control-panel/sender.js index 65d5c22d2..b579f0414 100644 --- a/lib/agent/plugins/control-panel/sender.js +++ b/lib/agent/plugins/control-panel/sender.js @@ -14,9 +14,7 @@ var make_request = function(what, data, opts, cb) { what = what.replace(/s$/, ''); opts.multipart = what == 'report'; - - var msg = 'Posting ' + what; - logger.info(msg); + logger.info('Posting ' + what); api.push[what](data, opts, function(err, resp) { bus.emit('response', what, err, resp); @@ -28,7 +26,7 @@ var send = function(what, data, opts, cb) { var opts = opts || {}; var done = function(err, resp) { - var str = err ? 'Got error: ' + err.message : 'Got ' + resp.statusCode + ' response: ' + resp.body.toString(); + var str = err ? 'Got error: ' + err.message : 'Got ' + resp.statusCode + ' response: ' + resp.body; logger.info(str); cb && cb(err, resp); } diff --git a/lib/agent/plugins/control-panel/test/main_load.js b/lib/agent/plugins/control-panel/test/main_load.js index 3b8459e55..dec0fafbb 100644 --- a/lib/agent/plugins/control-panel/test/main_load.js +++ b/lib/agent/plugins/control-panel/test/main_load.js @@ -24,6 +24,7 @@ describe('main.load', function() { // insert default values to config config.set('host', 'destination.com'); config.set('protocol', 'https'); + config.global = { get: function() {} } var background = false; @@ -391,4 +392,4 @@ describe('main.load', function() { }) -}) \ No newline at end of file +}) diff --git a/lib/agent/providers.js b/lib/agent/providers.js index 86768f75a..feb62a9af 100644 --- a/lib/agent/providers.js +++ b/lib/agent/providers.js @@ -71,17 +71,18 @@ var get = exports.get = function(name, cb) { callback = cb || function(){ /* noop */ }; var fire_callbacks = function(name, err, result) { - callbacks[name].forEach(function(fn){ + var list = callbacks[name]; + callbacks[name] = []; + + list.forEach(function(fn){ fn(err, result, name); }); - callbacks[name] = []; } if (name == 'report') name = 'stolen'; map(function(err, getters) { - if (err) return callback(err); if (getters[name]) { @@ -89,12 +90,15 @@ var get = exports.get = function(name, cb) { callbacks[name] = callbacks[name] || []; callbacks[name].push(callback); - if (callbacks[name].length > 1) + if (callbacks[name].length > 1) { return logger.info(name + ' already in progress.'); + } logger.debug('Fetching ' + name); getters[name](function(err, result) { + fire_callbacks(name, err, result); + if (!cb) { // only emit when no callback passed if (err) hooks.trigger('error', err); @@ -106,7 +110,6 @@ var get = exports.get = function(name, cb) { files.push(result.file); // keep a record so we remove it afterwards } - fire_callbacks(name, err, result); }); } else { diff --git a/lib/agent/transports/http/index.js b/lib/agent/transports/http/index.js index f46c07769..ea25b70e7 100644 --- a/lib/agent/transports/http/index.js +++ b/lib/agent/transports/http/index.js @@ -15,17 +15,17 @@ exports.defaults = function(opts) { } exports.get = function(url, opts, cb) { - return exports.request('GET', url, null, opts, cb) + return request('GET', url, null, opts, cb) } exports.post = function(url, data, opts, cb) { - return exports.request('POST', url, data, opts, cb) + return request('POST', url, data, opts, cb) } exports.put = function(url, data, opts, cb) { - return exports.request('PUT', url, data, opts, cb) + return request('PUT', url, data, opts, cb) } exports.del = function(url, data, opts, cb) { - return exports.request('DELETE', url, data, opts, cb) + return request('DELETE', url, data, opts, cb) } diff --git a/lib/agent/triggers/power/index.js b/lib/agent/triggers/power/index.js index a258a2c18..52c8e391a 100644 --- a/lib/agent/triggers/power/index.js +++ b/lib/agent/triggers/power/index.js @@ -9,7 +9,7 @@ var emitter, var check_battery_status = function() { - providers.get('battery_status', function(err, current){ + providers.get('battery_status', function(err, current) { if (err || !emitter) return; // console.log('Current: ' + current.state); @@ -31,7 +31,7 @@ exports.start = function(opts, cb){ if (err) return cb(err); power.on('state_changed', function(info){ - check_battery_status(info); + setTimeout(check_battery_status, 1500); }); power.on('low_power', function(info){ diff --git a/lib/conf/gui/linux/prey-config.glade b/lib/conf/gui/linux/prey-config.glade index fe5a37fe3..fb161411f 100644 --- a/lib/conf/gui/linux/prey-config.glade +++ b/lib/conf/gui/linux/prey-config.glade @@ -470,6 +470,19 @@ 120 + + + 280 + 30 + True + https://panel.preyproject.com/forgot + Forgot your password? + + + 150 + 160 + + 3 diff --git a/lib/conf/gui/mac/PreyConfig.app/Contents/MacOS/prey-config.py b/lib/conf/gui/mac/PreyConfig.app/Contents/MacOS/prey-config.py index 7c0510f24..85c111a82 100755 --- a/lib/conf/gui/mac/PreyConfig.app/Contents/MacOS/prey-config.py +++ b/lib/conf/gui/mac/PreyConfig.app/Contents/MacOS/prey-config.py @@ -48,7 +48,7 @@ } ################################################ -# paths and such +# paths and such SCRIPT_PATH = sys.path[0] @@ -60,7 +60,7 @@ def find_in_path(file): full_path = os.path.join('/'.join(segments), file) # print "Checking %s" % full_path if os.path.exists(full_path): - return full_path + return full_path else: path = segments.pop() @@ -421,6 +421,10 @@ def run_command(self, cmd): self.out = "Exception! %s" % e self.code = 1 + def open_pass_recovery_url(self): + url = "https://panel.preyproject.com/forgot" + res = NSWorkspace.sharedWorkspace().openURL_(NSURL.URLWithString_(url)) + ###################################################### # tab clicks @@ -466,7 +470,8 @@ def drawExistingUser(self, tab, name): elements = [] elements.append(self.drawTextInput('existing_email', 'Email', 15, 140)) elements.append(self.drawPasswordInput('existing_pass', 'Password', 15, 85)) - + elements.append(self.drawButton(NSMakeRect(15, 20, 420, 50), 'Forgot your password?', 'open_pass_recovery_url')) + for element in flatten(elements): tab.view().addSubview_(element) diff --git a/lib/conf/gui/windows/prey-config.exe b/lib/conf/gui/windows/prey-config.exe index 5040e7a1a..c60044a2e 100644 Binary files a/lib/conf/gui/windows/prey-config.exe and b/lib/conf/gui/windows/prey-config.exe differ diff --git a/lib/plugins.js b/lib/plugins.js index c1eef15b5..af14dd09d 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -14,7 +14,7 @@ var filter = function(arr) { // exports // init the loader. this provides all(), get(), require() and invoke() -var plugins = wink.init(__dirname + '/agent/plugins'); +var plugins = wink.init(__dirname + '/agent/plugins'); // called from common lib after config is loaded plugins.init = function(config_obj) { @@ -33,7 +33,7 @@ plugins.get_enabled = function() { var obj = config.get(config_key); - if (obj && obj.length) + if (obj && typeof obj == 'object') return filter(obj); return filter((obj || '').split(', ')) diff --git a/package.json b/package.json index 832a1492e..5ca0ea679 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "scrambler": "0.0.1", "serve-static": "^1.7.0", "sudoer": "^0.1.1", - "triggers": "^0.2.0", + "triggers": "^0.3.0", "tuna": "0.0.2", "underscore": "", "whenever": "0.0.3", diff --git a/test/bin_spec.js b/test/bin_spec.js index e3f96826c..a8c5a688a 100644 --- a/test/bin_spec.js +++ b/test/bin_spec.js @@ -1,7 +1,6 @@ var should = require('should'), path = require('path'), fs = require('fs'), - os = require('os'), exec = require('child_process').exec, spawn = require('child_process').spawn, is_windows = process.platform == 'win32'; @@ -11,7 +10,6 @@ var exec_env = process.env, // so we can override it later bin_path = path.join(__dirname, '..', 'bin'), bin_prey = path.join(bin_path, 'prey'), node_bin = path.join(bin_path, 'node'), - fake_log_file = path.join(os.tmpDir(), 'fake_test_log_file.log'), local_present = fs.existsSync(node_bin); if (is_windows) { @@ -40,22 +38,6 @@ function run_bin_prey(args, cb) { }, 1500); } -/* - -function mask_bin_prey(){ - var bin_prey_content = fs.readFileSync(bin_prey,'utf8'); - fs.renameSync(bin_prey, bin_prey + '.tmp'); - var fake_prey_content = bin_prey_content.replace('if (scr', - 'var require=function(str){console.log(str);console.log(process.argv);return 0;}\nif (scr'); - fs.writeFileSync(bin_prey, fake_prey_content, {mode: 0755}); -} - -function unmask_bin_prey(){ - fs.renameSync(bin_prey + '.tmp', bin_prey); -} - -*/ - describe('bin/prey', function(){ before(function(done) { @@ -77,19 +59,10 @@ describe('bin/prey', function(){ }); }); - after(function(done){ - fs.unlink(fake_log_file, done); - }); - - // we use a fake log file since when running via mocha - // the spawned process does not have a tty attached, so - // it assumes that it is running on background and prints - // any output to the default log file (/var/log/prey.log) it('uses local node binary', function(done){ - run_bin_prey(['-l', fake_log_file, '-N'], function(code) { + run_bin_prey(['-N'], function(code, out, err) { code.should.equal(0); - var read_version = fs.readFileSync(fake_log_file, 'utf8'); - read_version.should.containEql(node_versions.local); + out.should.containEql(node_versions.local); done(); }) }); @@ -107,7 +80,6 @@ describe('bin/prey', function(){ it('calls lib/agent/cli.js', function(done){ run_bin_prey(['-h'], function(code, out, err){ - // out.should.containEql('spreads its wings'); out.should.containEql('--logfile'); done(); }); @@ -135,34 +107,6 @@ describe('bin/prey', function(){ }); }); -/* - - describe('when called with `test` argument', function(){ - it('calls mocha', function(done){ - run_bin_prey(' test', function(err, out){ - var out_command = - is_windows ? 'node_modules\\.bin\\_mocha' - : 'node_modules/.bin/_mocha' - out.should.containEql(out_command); - done(); - }); - }); - - it('passes any other arguments too (eg. `--reporter nyan`)', function(done){ - run_bin_prey(' test --reporter nyan', function(err, out){ - var out_command = - is_windows ? 'node_modules\\.bin\\_mocha' - : 'node_modules/.bin/_mocha'; - out.should.containEql(out_command); - out.should.containEql('--reporter'); - out.should.containEql('nyan'); - done(); - }); - }); - }); - -*/ - describe('when called with unknown argument', function(){ it('returns unknown option', function(done){ run_bin_prey(['--hellomyfriend'], function(err, out, stderr){ diff --git a/test/lib/package.js b/test/lib/package.js index bec58d073..0da202023 100644 --- a/test/lib/package.js +++ b/test/lib/package.js @@ -6,6 +6,7 @@ var fs = require('fs'), sinon = require('sinon'), should = require('should'), rmdir = require('rimraf'), + buckle = require('buckle'), child_process = require('child_process'), helpers = require('../helpers');