| /* |
| Copyright © 2012-2014 Azer Koculu. All Rights Reserved. |
| https://github.com/azer/node-virtualbox |
| |
| Additional contributions by Michael Sanford, Steffen Roegner, Jakub Lekstan, |
| Christopher Najewicz, Cédric Belin, and Jason Robitaille. |
| |
| Redistribution and use in source and binary forms, with or without modification, |
| are permitted provided that the following conditions are met: |
| |
| 1. Redistributions of source code must retain the above copyright notice, |
| this list of conditions and the following disclaimer. |
| |
| 2. Redistributions in binary form must reproduce the above copyright notice, |
| this list of conditions and the following disclaimer in the documentation and/or |
| other materials provided with the distribution. |
| |
| 3. The name of the author may not be used to endorse or promote products derived |
| from this software without specific prior written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY |
| EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
| SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
| OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
| GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
| EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| "use strict"; |
| |
| var exec = require('child_process').exec, |
| host_platform = process.platform, |
| vBoxManageBinary, |
| known_OS_types = { |
| WINDOWS: 'windows', |
| MAC: 'mac', |
| LINUX: 'linux' |
| }; |
| |
| |
| // Host operating system |
| if (/^win/.test(host_platform)) { |
| |
| // Path may not contain VBoxManage.exe but it provides this environment variable |
| var vBoxInstallPath = process.env.VBOX_INSTALL_PATH || process.env.VBOX_MSI_INSTALL_PATH; |
| vBoxManageBinary = '"' + vBoxInstallPath + '\\VBoxManage.exe' + '" '; |
| |
| } else if (/^darwin/.test(host_platform) || /^linux/.test(host_platform)) { |
| |
| // Mac OS X and most Linux use the same binary name, in the path |
| vBoxManageBinary = 'vboxmanage '; |
| |
| } else { |
| |
| // Otherwise (e.g., SunOS) hope it's in the path |
| vBoxManageBinary = 'vboxmanage '; |
| |
| } |
| |
| function command(cmd, callback) { |
| exec(cmd, function(err, stdout, stderr) { |
| |
| if (!err && stderr && cmd.indexOf("pause") !== -1 && cmd.indexOf("savestate") !== -1) { |
| err = new Error(stderr); |
| } |
| |
| callback(err, stdout); |
| }); |
| } |
| |
| function vboxcontrol(cmd, callback) { |
| command('VBoxControl ' + cmd, callback); |
| } |
| |
| function vboxmanage(cmd, callback) { |
| command(vBoxManageBinary + cmd, callback); |
| } |
| |
| function pause(vmname, callback) { |
| vboxmanage('controlvm "' + vmname + '" pause', function(error, stdout) { |
| callback(error); |
| }); |
| } |
| |
| function list(callback) { |
| vboxmanage('list "runningvms"', function(error, stdout) { |
| var _list = {}; |
| var _runningvms = parse_listdata(stdout); |
| vboxmanage('list "vms"', function(error, full_stdout) { |
| var _all = parse_listdata(full_stdout); |
| var _keys = Object.keys(_all); |
| for (var _i = 0; _i < _keys.length; _i += 1) { |
| var _key = _keys[_i]; |
| if (_runningvms[_key]) { |
| _all[_key].running = true; |
| } else { |
| _all[_key].running = false; |
| } |
| } |
| callback(_all, error); |
| }); |
| }); |
| } |
| |
| function parse_listdata(raw_data) { |
| var _raw = raw_data.split(/\r?\n/g); |
| var _data = {}; |
| if (_raw.length > 0) { |
| for (var _i = 0; _i < _raw.length; _i += 1) { |
| var _line = _raw[_i]; |
| if (_line === '') { |
| continue; |
| } |
| // "centos6" {64ec13bb-5889-4352-aee9-0f1c2a17923d} |
| var rePattern = /^"(.+)" \{(.+)\}$/; |
| var arrMatches = _line.match(rePattern); |
| // {'64ec13bb-5889-4352-aee9-0f1c2a17923d': 'centos6'} |
| if (arrMatches && arrMatches.length === 3) { |
| _data[arrMatches[2].toString()] = { |
| name: arrMatches[1].toString() |
| }; |
| } |
| } |
| } |
| return _data; |
| } |
| |
| function info(vmname, callback) { |
| vboxmanage('showvminfo "' + vmname + '" --machinereadable', function(error, stdout) { |
| var info = {}; |
| var data = stdout.split(/\r?\n/g); |
| for(var i=0; i<data.length; i++) { |
| if(data[i].length>0) { |
| var index = data[i].indexOf("="); |
| if(index>0) { |
| var key = parse_infoentry(data[i].substring(0, index)); |
| var value = parse_infoentry(data[i].substring(index+1)); |
| info[key] = value; |
| } |
| } |
| } |
| callback(info); |
| }); |
| } |
| |
| function parse_infoentry(entry) { |
| if(entry.length>1 && entry.charAt(0)=="\"") { |
| entry = entry.substring(1); |
| } |
| if(entry.length>1 && entry.charAt(entry.length-1)=="\"") { |
| entry = entry.substring(0, entry.length-1); |
| } |
| return entry; |
| } |
| |
| function reset(vmname, callback) { |
| vboxmanage('controlvm "' + vmname + '" reset', function(error, stdout) { |
| callback(error); |
| }); |
| } |
| |
| function resume(vmname, callback) { |
| vboxmanage('controlvm "' + vmname + '" resume', function(error, stdout) { |
| callback(error); |
| }); |
| } |
| |
| function start(vmname, use_gui, callback) { |
| var start_opts = ' --type '; |
| if ((typeof use_gui) === 'function') { |
| callback = use_gui; |
| use_gui = false; |
| } |
| start_opts += (use_gui ? 'gui' : 'headless'); |
| |
| vboxmanage('-nologo startvm "' + vmname + '"' + start_opts, function(error, stdout) { |
| if (error && /VBOX_E_INVALID_OBJECT_STATE/.test(error.message)) { |
| error = undefined; |
| } |
| callback(error); |
| }); |
| } |
| |
| function stop(vmname, callback) { |
| vboxmanage('controlvm "' + vmname + '" savestate', function(error, stdout) { |
| callback(error); |
| }); |
| } |
| |
| function savestate(vmname, callback) { |
| stop(vmname, callback); |
| } |
| |
| function poweroff(vmname, callback) { |
| vboxmanage('controlvm "' + vmname + '" poweroff', function(error, stdout) { |
| callback(error); |
| }); |
| } |
| |
| function acpipowerbutton(vmname, callback) { |
| vboxmanage('controlvm "' + vmname + '" acpipowerbutton', function(error, stdout) { |
| callback(error); |
| }); |
| } |
| |
| function acpisleepbutton(vmname, callback) { |
| vboxmanage('controlvm "' + vmname + '" acpisleepbutton', function(error, stdout) { |
| callback(error); |
| }); |
| } |
| |
| function vmExec(options, callback) { |
| var vm = options.vm || options.name || options.vmname || options.title, |
| username = options.user || options.username || 'Guest', |
| password = options.pass || options.passwd || options.password, |
| path = options.path || options.cmd || options.command || options.exec || options.execute || options.run, |
| cmd, |
| params = options.params || options.parameters || options.args; |
| |
| if (Array.isArray(params)) { |
| params = params.join(" "); |
| } |
| |
| if (params === undefined) { |
| params = ""; |
| } |
| |
| guestproperty.os(vm, getOSTypeCb); |
| |
| function getOSTypeCb(os_type) { |
| var cmd = 'guestcontrol "' + vm + '"'; |
| |
| switch (os_type) { |
| case known_OS_types.WINDOWS: |
| path = path.replace(/\\/g, '\\\\'); |
| cmd += ' execute --image "cmd.exe" --username ' + username + (password ? ' --password ' + password : '') + ' -- "/c" "' + path + '" "' + params + '"'; |
| break; |
| case known_OS_types.MAC: |
| cmd += ' execute --image "/usr/bin/open -a" --username ' + username + (password ? ' --password ' + password : '') + ' -- "/c" "' + path + '" "' + params + '"'; |
| break; |
| case known_OS_types.LINUX: |
| cmd += ' execute --image "/bin/sh" --username ' + username + (password ? ' --password ' + password : '') + ' -- "/c" "' + path + '" "' + params + '"'; |
| break; |
| default: |
| break; |
| } |
| |
| vboxmanage(cmd, function(error, stdout) { |
| callback(error); |
| }); |
| } |
| |
| } |
| |
| function vmKill(options, callback) { |
| options = options || {}; |
| var vm = options.vm || options.name || options.vmname || options.title, |
| path = options.path || options.cmd || options.command || options.exec || options.execute || options.run, |
| image_name = options.image_name || path, |
| cmd = 'guestcontrol "' + vm + '" process kill'; |
| |
| guestproperty.os(vm, function(os_type) { |
| switch (os_type) { |
| case known_OS_types.WINDOWS: |
| vmExec({ |
| vm: vm, |
| user: options.user, |
| password: options.password, |
| path: 'C:\\Windows\\System32\\taskkill.exe /im ', |
| params: image_name |
| }, callback); |
| break; |
| case known_OS_types.MAC: |
| case known_OS_types.LINUX: |
| vmExec({ |
| vm: vm, |
| user: options.user, |
| password: options.password, |
| path: 'sudo killall ', |
| params: image_name |
| }, callback); |
| break; |
| } |
| }); |
| |
| } |
| |
| var guestproperty = { |
| get: function(options, callback) { |
| var vm = options.vm || options.name || options.vmname || options.title, |
| key = options.key, |
| value = options.defaultValue || options.value; |
| |
| guestproperty.os(vm, getOSTypeCallback); |
| |
| function getOSTypeCallback(os_type) { |
| var cmd = 'guestproperty get "' + vm + '" ' + key; |
| vboxmanage(cmd, function(error, stdout) { |
| if (error) { |
| throw error; |
| } |
| var value = stdout.substr(stdout.indexOf(':') + 1).trim(); |
| if (value === 'No value set!') { |
| value = undefined; |
| } |
| callback(value); |
| }); |
| } |
| |
| }, |
| |
| os_type: null, // cached |
| |
| os: function(vmname, callback) { |
| function getOSTypeCallback(error, stdout, stderr) { |
| if (error) { |
| throw error; |
| } |
| |
| // The ostype is matched against the ID attribute of 'vboxmanage list ostypes' |
| if (stdout.indexOf('ostype="Windows') !== -1) { |
| guestproperty.os_type = known_OS_types.WINDOWS; |
| } else if (stdout.indexOf('ostype="MacOS') !== -1) { |
| guestproperty.os_type = known_OS_types.MAC; |
| } else { |
| guestproperty.os_type = known_OS_types.LINUX; |
| } |
| callback(guestproperty.os_type); |
| } |
| |
| if (guestproperty.os_type) { |
| return callback(guestproperty.os_type); |
| } |
| |
| try { |
| exec(vBoxManageBinary + 'showvminfo -machinereadable "' + vmname + '"', getOSTypeCallback); |
| } catch (e) { |
| console.error('Could not showvminfo for ' + vmname); |
| } |
| } |
| |
| }; |
| |
| module.exports = { |
| 'exec': vmExec, |
| 'kill': vmKill, |
| 'list': list, |
| 'info': info, |
| 'pause': pause, |
| 'reset': reset, |
| 'resume': resume, |
| 'start': start, |
| 'stop': stop, |
| 'savestate': savestate, |
| 'poweroff': poweroff, |
| 'acpisleepbutton': acpisleepbutton, |
| 'acpipowerbutton': acpipowerbutton, |
| 'guestproperty': guestproperty |
| }; |