diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Frontends/VBoxShell | |
parent | Initial commit. (diff) | |
download | virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.tar.xz virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Frontends/VBoxShell')
-rw-r--r-- | src/VBox/Frontends/VBoxShell/Makefile.kmk | 41 | ||||
-rw-r--r-- | src/VBox/Frontends/VBoxShell/vboxinfo.vb | 60 | ||||
-rw-r--r-- | src/VBox/Frontends/VBoxShell/vboxinfo.vbs | 72 | ||||
-rwxr-xr-x | src/VBox/Frontends/VBoxShell/vboxshell.py | 3635 |
4 files changed, 3808 insertions, 0 deletions
diff --git a/src/VBox/Frontends/VBoxShell/Makefile.kmk b/src/VBox/Frontends/VBoxShell/Makefile.kmk new file mode 100644 index 00000000..65ac3450 --- /dev/null +++ b/src/VBox/Frontends/VBoxShell/Makefile.kmk @@ -0,0 +1,41 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the shell frontend. +# + +# +# Copyright (C) 2009-2022 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# SPDX-License-Identifier: GPL-3.0-only +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +INSTALLS += VBoxShell +VBoxShell_INST = $(INST_SDK)/bindings/ +VBoxShell_SOURCES = \ + vboxshell.py=>glue/python/sample/vboxshell.py \ + vboxinfo.vbs=>mscom/vbs/sample/vboxinfo.vbs \ + vboxinfo.vb=>mscom/vb/sample/vboxinfo.vb + + + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/Frontends/VBoxShell/vboxinfo.vb b/src/VBox/Frontends/VBoxShell/vboxinfo.vb new file mode 100644 index 00000000..769e3681 --- /dev/null +++ b/src/VBox/Frontends/VBoxShell/vboxinfo.vb @@ -0,0 +1,60 @@ +' $Id: vboxinfo.vb $
+'' @file
+' ???
+'
+
+'
+' Copyright (C) 2009-2022 Oracle and/or its affiliates.
+'
+' This file is part of VirtualBox base platform packages, as
+' available from https://www.virtualbox.org.
+'
+' This program is free software; you can redistribute it and/or
+' modify it under the terms of the GNU General Public License
+' as published by the Free Software Foundation, in version 3 of the
+' License.
+'
+' This program is distributed in the hope that it will be useful, but
+' WITHOUT ANY WARRANTY; without even the implied warranty of
+' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+' General Public License for more details.
+'
+' You should have received a copy of the GNU General Public License
+' along with this program; if not, see <https://www.gnu.org/licenses>.
+'
+' SPDX-License-Identifier: GPL-3.0-only
+'
+
+Imports System
+Imports System.Drawing
+Imports System.Windows.Forms
+
+Module Module1
+
+ Sub Main()
+ Dim vb As VirtualBox.IVirtualBox
+ Dim listBox As New ListBox()
+ Dim form As New Form
+
+ vb = CreateObject("VirtualBox.VirtualBox")
+
+ form.Text = "VirtualBox version " & vb.Version
+ form.Size = New System.Drawing.Size(400, 320)
+ form.Location = New System.Drawing.Point(10, 10)
+
+ listBox.Size = New System.Drawing.Size(350, 200)
+ listBox.Location = New System.Drawing.Point(10, 10)
+
+ For Each m In vb.Machines
+ listBox.Items.Add(m.Name & " [" & m.Id & "]")
+ Next
+
+ form.Controls.Add(listBox)
+
+ 'form.ShowDialog()
+ form.Show()
+ MsgBox("OK")
+ End Sub
+
+End Module
+
diff --git a/src/VBox/Frontends/VBoxShell/vboxinfo.vbs b/src/VBox/Frontends/VBoxShell/vboxinfo.vbs new file mode 100644 index 00000000..f6cb59c1 --- /dev/null +++ b/src/VBox/Frontends/VBoxShell/vboxinfo.vbs @@ -0,0 +1,72 @@ +' $Id: vboxinfo.vbs $
+'' @file
+' ???
+'
+
+'
+' Copyright (C) 2009-2022 Oracle and/or its affiliates.
+'
+' This file is part of VirtualBox base platform packages, as
+' available from https://www.virtualbox.org.
+'
+' This program is free software; you can redistribute it and/or
+' modify it under the terms of the GNU General Public License
+' as published by the Free Software Foundation, in version 3 of the
+' License.
+'
+' This program is distributed in the hope that it will be useful, but
+' WITHOUT ANY WARRANTY; without even the implied warranty of
+' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+' General Public License for more details.
+'
+' You should have received a copy of the GNU General Public License
+' along with this program; if not, see <https://www.gnu.org/licenses>.
+'
+' SPDX-License-Identifier: GPL-3.0-only
+'
+
+Sub Print(str)
+ Wscript.Echo str
+End Sub
+
+Sub StartVm(vb, mach)
+ Dim session, progress
+
+ Set session = CreateObject("VirtualBox.Session")
+ Set progress = vb.openRemoteSession(session, mach.id, "gui", "")
+ progress.waitForCompletion(-1)
+ session.close()
+End Sub
+
+
+Sub StopVm(vb, mach)
+ Dim session, progress
+
+ Set session = CreateObject("VirtualBox.Session")
+ vb.openExistingSession session, mach.id
+ session.console.powerDown().waitForCompletion(-1)
+ session.close()
+End Sub
+
+
+Sub Main
+ Dim vb, mach
+
+ set vb = CreateObject("VirtualBox.VirtualBox")
+ Print "VirtualBox version " & vb.version
+
+ ' Safe arrays not fully functional from Visual Basic Script, as we
+ ' return real safe arrays, not ones wrapped to VARIANT and VBS engine
+ ' gets confused. Until then, explicitly find VM by name.
+ ' May wish to use hack like one described in
+ ' http://www.tech-archive.net/Archive/Excel/microsoft.public.excel.programming/2006-05/msg02796.html to handle safearrays
+ ' if desperate
+
+ Set mach = vb.findMachine("Win")
+ Print "Machine: " & mach.name & " ID: " & mach.id
+
+ StartVm vb, mach
+End Sub
+
+Main
+
diff --git a/src/VBox/Frontends/VBoxShell/vboxshell.py b/src/VBox/Frontends/VBoxShell/vboxshell.py new file mode 100755 index 00000000..ecb16a1b --- /dev/null +++ b/src/VBox/Frontends/VBoxShell/vboxshell.py @@ -0,0 +1,3635 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# $Id: vboxshell.py $ + +""" +VirtualBox Python Shell. + +This program is a simple interactive shell for VirtualBox. You can query +information and issue commands from a simple command line. + +It also provides you with examples on how to use VirtualBox's Python API. +This shell is even somewhat documented, supports TAB-completion and +history if you have Python readline installed. + +Finally, shell allows arbitrary custom extensions, just create +.VirtualBox/shexts/ and drop your extensions there. + Enjoy. + +P.S. Our apologies for the code quality. +""" + +from __future__ import print_function + +__copyright__ = \ +""" +Copyright (C) 2009-2022 Oracle and/or its affiliates. + +This file is part of VirtualBox base platform packages, as +available from https://www.virtualbox.org. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, in version 3 of the +License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see <https://www.gnu.org/licenses>. + +SPDX-License-Identifier: GPL-3.0-only +""" +__version__ = "$Revision: 153224 $" + + +import gc +import os +import sys +import traceback +import shlex +import time +import re +import platform +from optparse import OptionParser +from pprint import pprint + + +# +# Global Variables +# +g_fBatchMode = False +g_sScriptFile = None +g_sCmd = None +g_fHasReadline = True +try: + import readline + import rlcompleter +except ImportError: + g_fHasReadline = False + +g_sPrompt = "vbox> " + +g_fHasColors = True +g_dTermColors = { + 'red': '\033[31m', + 'blue': '\033[94m', + 'green': '\033[92m', + 'yellow': '\033[93m', + 'magenta': '\033[35m', + 'cyan': '\033[36m' +} + + + +def colored(strg, color): + """ + Translates a string to one including coloring settings, if enabled. + """ + if not g_fHasColors: + return strg + col = g_dTermColors.get(color, None) + if col: + return col+str(strg)+'\033[0m' + return strg + +if g_fHasReadline: + class CompleterNG(rlcompleter.Completer): + def __init__(self, dic, ctx): + self.ctx = ctx + rlcompleter.Completer.__init__(self, dic) + + def complete(self, text, state): + """ + taken from: + http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496812 + """ + if False and text == "": + return ['\t', None][state] + else: + return rlcompleter.Completer.complete(self, text, state) + + def canBePath(self, _phrase, word): + return word.startswith('/') + + def canBeCommand(self, phrase, _word): + spaceIdx = phrase.find(" ") + begIdx = readline.get_begidx() + firstWord = (spaceIdx == -1 or begIdx < spaceIdx) + if firstWord: + return True + if phrase.startswith('help'): + return True + return False + + def canBeMachine(self, phrase, word): + return not self.canBePath(phrase, word) and not self.canBeCommand(phrase, word) + + def global_matches(self, text): + """ + Compute matches when text is a simple name. + Return a list of all names currently defined + in self.namespace that match. + """ + + matches = [] + phrase = readline.get_line_buffer() + + try: + if self.canBePath(phrase, text): + (directory, rest) = os.path.split(text) + c = len(rest) + for word in os.listdir(directory): + if c == 0 or word[:c] == rest: + matches.append(os.path.join(directory, word)) + + if self.canBeCommand(phrase, text): + c = len(text) + for lst in [ self.namespace ]: + for word in lst: + if word[:c] == text: + matches.append(word) + + if self.canBeMachine(phrase, text): + c = len(text) + for mach in getMachines(self.ctx, False, True): + # although it has autoconversion, we need to cast + # explicitly for subscripts to work + word = re.sub("(?<!\\\\) ", "\\ ", str(mach.name)) + if word[:c] == text: + matches.append(word) + word = str(mach.id) + if word[:c] == text: + matches.append(word) + + except Exception as e: + printErr(self.ctx, e) + if g_fVerbose: + traceback.print_exc() + + return matches + +def autoCompletion(cmds, ctx): + if not g_fHasReadline: + return + + comps = {} + for (key, _value) in list(cmds.items()): + comps[key] = None + completer = CompleterNG(comps, ctx) + readline.set_completer(completer.complete) + delims = readline.get_completer_delims() + readline.set_completer_delims(re.sub("[\\./-]", "", delims)) # remove some of the delimiters + readline.parse_and_bind("set editing-mode emacs") + # OSX need it + if platform.system() == 'Darwin': + # see http://www.certif.com/spec_help/readline.html + readline.parse_and_bind ("bind ^I rl_complete") + readline.parse_and_bind ("bind ^W ed-delete-prev-word") + # Doesn't work well + # readline.parse_and_bind ("bind ^R em-inc-search-prev") + readline.parse_and_bind("tab: complete") + + +g_fVerbose = False + +def split_no_quotes(s): + return shlex.split(s) + +def progressBar(ctx, progress, wait=1000): + try: + while not progress.completed: + print("%s %%\r" % (colored(str(progress.percent), 'red')), end="") + sys.stdout.flush() + progress.waitForCompletion(wait) + ctx['global'].waitForEvents(0) + if int(progress.resultCode) != 0: + reportError(ctx, progress) + return 1 + except KeyboardInterrupt: + print("Interrupted.") + ctx['interrupt'] = True + if progress.cancelable: + print("Canceling task...") + progress.cancel() + return 0 + +def printErr(_ctx, e): + oVBoxMgr = _ctx['global'] + if oVBoxMgr.xcptIsOurXcptKind(e): + print(colored('%s: %s' % (oVBoxMgr.xcptToString(e), oVBoxMgr.xcptGetMessage(e)), 'red')) + else: + print(colored(str(e), 'red')) + +def reportError(_ctx, progress): + errorinfo = progress.errorInfo + if errorinfo: + print(colored("Error in module '%s': %s" % (errorinfo.component, errorinfo.text), 'red')) + +def colCat(_ctx, strg): + return colored(strg, 'magenta') + +def colVm(_ctx, vmname): + return colored(vmname, 'blue') + +def colPath(_ctx, path): + return colored(path, 'green') + +def colSize(_ctx, byte): + return colored(byte, 'red') + +def colPci(_ctx, pcidev): + return colored(pcidev, 'green') + +def colDev(_ctx, pcidev): + return colored(pcidev, 'cyan') + +def colSizeM(_ctx, mbyte): + return colored(str(mbyte)+'M', 'red') + +def createVm(ctx, name, kind): + vbox = ctx['vb'] + mach = vbox.createMachine("", name, [], kind, "") + mach.saveSettings() + print("created machine with UUID", mach.id) + vbox.registerMachine(mach) + # update cache + getMachines(ctx, True) + +def removeVm(ctx, mach): + uuid = mach.id + print("removing machine ", mach.name, "with UUID", uuid) + cmdClosedVm(ctx, mach, detachVmDevice, ["ALL"]) + disks = mach.unregister(ctx['global'].constants.CleanupMode_Full) + if mach: + progress = mach.deleteConfig(disks) + if progressBar(ctx, progress, 100) and int(progress.resultCode) == 0: + print("Success!") + else: + reportError(ctx, progress) + # update cache + getMachines(ctx, True) + +def startVm(ctx, mach, vmtype): + vbox = ctx['vb'] + perf = ctx['perf'] + session = ctx['global'].getSessionObject() + asEnv = [] + progress = mach.launchVMProcess(session, vmtype, asEnv) + if progressBar(ctx, progress, 100) and int(progress.resultCode) == 0: + # we ignore exceptions to allow starting VM even if + # perf collector cannot be started + if perf: + try: + perf.setup(['*'], [mach], 10, 15) + except Exception as e: + printErr(ctx, e) + if g_fVerbose: + traceback.print_exc() + session.unlockMachine() + +class CachedMach: + def __init__(self, mach): + if mach.accessible: + self.name = mach.name + else: + self.name = '<inaccessible>' + self.id = mach.id + +def cacheMachines(_ctx, lst): + result = [] + for mach in lst: + elem = CachedMach(mach) + result.append(elem) + return result + +def getMachines(ctx, invalidate = False, simple=False): + if ctx['vb'] is not None: + if ctx['_machlist'] is None or invalidate: + ctx['_machlist'] = ctx['global'].getArray(ctx['vb'], 'machines') + ctx['_machlistsimple'] = cacheMachines(ctx, ctx['_machlist']) + if simple: + return ctx['_machlistsimple'] + else: + return ctx['_machlist'] + else: + return [] + +def asState(var): + if var: + return colored('on', 'green') + else: + return colored('off', 'green') + +def asFlag(var): + if var: + return 'yes' + else: + return 'no' + +def getFacilityStatus(ctx, guest, facilityType): + (status, _timestamp) = guest.getFacilityStatus(facilityType) + return asEnumElem(ctx, 'AdditionsFacilityStatus', status) + +def perfStats(ctx, mach): + if not ctx['perf']: + return + for metric in ctx['perf'].query(["*"], [mach]): + print(metric['name'], metric['values_as_string']) + +def guestExec(ctx, machine, console, cmds): + exec(cmds) + +def printMouseEvent(_ctx, mev): + print("Mouse: mode=%d x=%d y=%d z=%d w=%d buttons=%x" % (mev.mode, mev.x, mev.y, mev.z, mev.w, mev.buttons)) + +def printKbdEvent(ctx, kev): + print("Kbd: ", ctx['global'].getArray(kev, 'scancodes')) + +def printMultiTouchEvent(ctx, mtev): + print("MultiTouch: %s contacts=%d time=%d" \ + % ("touchscreen" if mtev.isTouchScreen else "touchpad", mtev.contactCount, mtev.scanTime)) + xPositions = ctx['global'].getArray(mtev, 'xPositions') + yPositions = ctx['global'].getArray(mtev, 'yPositions') + contactIds = ctx['global'].getArray(mtev, 'contactIds') + contactFlags = ctx['global'].getArray(mtev, 'contactFlags') + + for i in range(0, mtev.contactCount): + print(" [%d] %d,%d %d %d" % (i, xPositions[i], yPositions[i], contactIds[i], contactFlags[i])) + +def monitorSource(ctx, eventSource, active, dur): + def handleEventImpl(event): + evtype = event.type + print("got event: %s %s" % (str(evtype), asEnumElem(ctx, 'VBoxEventType', evtype))) + if evtype == ctx['global'].constants.VBoxEventType_OnMachineStateChanged: + scev = ctx['global'].queryInterface(event, 'IMachineStateChangedEvent') + if scev: + print("machine state event: mach=%s state=%s" % (scev.machineId, scev.state)) + elif evtype == ctx['global'].constants.VBoxEventType_OnSnapshotTaken: + stev = ctx['global'].queryInterface(event, 'ISnapshotTakenEvent') + if stev: + print("snapshot taken event: mach=%s snap=%s" % (stev.machineId, stev.snapshotId)) + elif evtype == ctx['global'].constants.VBoxEventType_OnGuestPropertyChanged: + gpcev = ctx['global'].queryInterface(event, 'IGuestPropertyChangedEvent') + if gpcev: + if gpcev.fWasDeleted is True: + print("property %s was deleted" % (gpcev.name)) + else: + print("guest property change: name=%s value=%s flags='%s'" % + (gpcev.name, gpcev.value, gpcev.flags)) + elif evtype == ctx['global'].constants.VBoxEventType_OnMousePointerShapeChanged: + psev = ctx['global'].queryInterface(event, 'IMousePointerShapeChangedEvent') + if psev: + shape = ctx['global'].getArray(psev, 'shape') + if shape is None: + print("pointer shape event - empty shape") + else: + print("pointer shape event: w=%d h=%d shape len=%d" % (psev.width, psev.height, len(shape))) + elif evtype == ctx['global'].constants.VBoxEventType_OnGuestMouse: + mev = ctx['global'].queryInterface(event, 'IGuestMouseEvent') + if mev: + printMouseEvent(ctx, mev) + elif evtype == ctx['global'].constants.VBoxEventType_OnGuestKeyboard: + kev = ctx['global'].queryInterface(event, 'IGuestKeyboardEvent') + if kev: + printKbdEvent(ctx, kev) + elif evtype == ctx['global'].constants.VBoxEventType_OnGuestMultiTouch: + mtev = ctx['global'].queryInterface(event, 'IGuestMultiTouchEvent') + if mtev: + printMultiTouchEvent(ctx, mtev) + + class EventListener(object): + def __init__(self, arg): + pass + + def handleEvent(self, event): + try: + # a bit convoluted QI to make it work with MS COM + handleEventImpl(ctx['global'].queryInterface(event, 'IEvent')) + except: + traceback.print_exc() + pass + + if active: + listener = ctx['global'].createListener(EventListener) + else: + listener = eventSource.createListener() + registered = False + if dur == -1: + # not infinity, but close enough + dur = 100000 + try: + eventSource.registerListener(listener, [ctx['global'].constants.VBoxEventType_Any], active) + registered = True + end = time.time() + dur + while time.time() < end: + if active: + ctx['global'].waitForEvents(500) + else: + event = eventSource.getEvent(listener, 500) + if event: + handleEventImpl(event) + # otherwise waitable events will leak (active listeners ACK automatically) + eventSource.eventProcessed(listener, event) + # We need to catch all exceptions here, otherwise listener will never be unregistered + except: + traceback.print_exc() + pass + if listener and registered: + eventSource.unregisterListener(listener) + + +g_tsLast = 0 +def recordDemo(ctx, console, filename, dur): + demo = open(filename, 'w') + header = "VM=" + console.machine.name + "\n" + demo.write(header) + + global g_tsLast + g_tsLast = time.time() + + def stamp(): + global g_tsLast + tsCur = time.time() + timePassed = int((tsCur-g_tsLast)*1000) + g_tsLast = tsCur + return timePassed + + def handleEventImpl(event): + evtype = event.type + #print("got event: %s %s" % (str(evtype), asEnumElem(ctx, 'VBoxEventType', evtype))) + if evtype == ctx['global'].constants.VBoxEventType_OnGuestMouse: + mev = ctx['global'].queryInterface(event, 'IGuestMouseEvent') + if mev: + line = "%d: m %d %d %d %d %d %d\n" % (stamp(), mev.mode, mev.x, mev.y, mev.z, mev.w, mev.buttons) + demo.write(line) + elif evtype == ctx['global'].constants.VBoxEventType_OnGuestKeyboard: + kev = ctx['global'].queryInterface(event, 'IGuestKeyboardEvent') + if kev: + line = "%d: k %s\n" % (stamp(), str(ctx['global'].getArray(kev, 'scancodes'))) + demo.write(line) + + listener = console.eventSource.createListener() + registered = False + # we create an aggregated event source to listen for multiple event sources (keyboard and mouse in our case) + agg = console.eventSource.createAggregator([console.keyboard.eventSource, console.mouse.eventSource]) + demo = open(filename, 'w') + header = "VM=" + console.machine.name + "\n" + demo.write(header) + if dur == -1: + # not infinity, but close enough + dur = 100000 + try: + agg.registerListener(listener, [ctx['global'].constants.VBoxEventType_Any], False) + registered = True + end = time.time() + dur + while time.time() < end: + event = agg.getEvent(listener, 1000) + if event: + handleEventImpl(event) + # keyboard/mouse events aren't waitable, so no need for eventProcessed + # We need to catch all exceptions here, otherwise listener will never be unregistered + except: + traceback.print_exc() + pass + demo.close() + if listener and registered: + agg.unregisterListener(listener) + + +def playbackDemo(ctx, console, filename, dur): + demo = open(filename, 'r') + + if dur == -1: + # not infinity, but close enough + dur = 100000 + + header = demo.readline() + print("Header is", header) + basere = re.compile(r'(?P<s>\d+): (?P<t>[km]) (?P<p>.*)') + mre = re.compile(r'(?P<a>\d+) (?P<x>-*\d+) (?P<y>-*\d+) (?P<z>-*\d+) (?P<w>-*\d+) (?P<b>-*\d+)') + kre = re.compile(r'\d+') + + kbd = console.keyboard + mouse = console.mouse + + try: + end = time.time() + dur + for line in demo: + if time.time() > end: + break + match = basere.search(line) + if match is None: + continue + + rdict = match.groupdict() + stamp = rdict['s'] + params = rdict['p'] + rtype = rdict['t'] + + time.sleep(float(stamp)/1000) + + if rtype == 'k': + codes = kre.findall(params) + #print("KBD:", codes) + kbd.putScancodes(codes) + elif rtype == 'm': + mm = mre.search(params) + if mm is not None: + mdict = mm.groupdict() + if mdict['a'] == '1': + # absolute + #print("MA: ", mdict['x'], mdict['y'], mdict['z'], mdict['b']) + mouse.putMouseEventAbsolute(int(mdict['x']), int(mdict['y']), int(mdict['z']), int(mdict['w']), int(mdict['b'])) + else: + #print("MR: ", mdict['x'], mdict['y'], mdict['b']) + mouse.putMouseEvent(int(mdict['x']), int(mdict['y']), int(mdict['z']), int(mdict['w']), int(mdict['b'])) + + # We need to catch all exceptions here, to close file + except KeyboardInterrupt: + ctx['interrupt'] = True + except: + traceback.print_exc() + pass + demo.close() + + +def takeScreenshotOld(_ctx, console, args): + from PIL import Image + display = console.display + if len(args) > 0: + f = args[0] + else: + f = "/tmp/screenshot.png" + if len(args) > 3: + screen = int(args[3]) + else: + screen = 0 + (fbw, fbh, _fbbpp, fbx, fby, _) = display.getScreenResolution(screen) + if len(args) > 1: + w = int(args[1]) + else: + w = fbw + if len(args) > 2: + h = int(args[2]) + else: + h = fbh + + print("Saving screenshot (%d x %d) screen %d in %s..." % (w, h, screen, f)) + data = display.takeScreenShotToArray(screen, w, h, ctx['const'].BitmapFormat_RGBA) + size = (w, h) + mode = "RGBA" + im = Image.frombuffer(mode, size, str(data), "raw", mode, 0, 1) + im.save(f, "PNG") + +def takeScreenshot(_ctx, console, args): + display = console.display + if len(args) > 0: + f = args[0] + else: + f = "/tmp/screenshot.png" + if len(args) > 3: + screen = int(args[3]) + else: + screen = 0 + (fbw, fbh, _fbbpp, fbx, fby, _) = display.getScreenResolution(screen) + if len(args) > 1: + w = int(args[1]) + else: + w = fbw + if len(args) > 2: + h = int(args[2]) + else: + h = fbh + + print("Saving screenshot (%d x %d) screen %d in %s..." % (w, h, screen, f)) + data = display.takeScreenShotToArray(screen, w, h, ctx['const'].BitmapFormat_PNG) + pngfile = open(f, 'wb') + pngfile.write(data) + pngfile.close() + +def teleport(ctx, _session, console, args): + if args[0].find(":") == -1: + print("Use host:port format for teleport target") + return + (host, port) = args[0].split(":") + if len(args) > 1: + passwd = args[1] + else: + passwd = "" + + if len(args) > 2: + maxDowntime = int(args[2]) + else: + maxDowntime = 250 + + port = int(port) + print("Teleporting to %s:%d..." % (host, port)) + progress = console.teleport(host, port, passwd, maxDowntime) + if progressBar(ctx, progress, 100) and int(progress.resultCode) == 0: + print("Success!") + else: + reportError(ctx, progress) + + +def guestStats(ctx, console, args): + guest = console.guest + # we need to set up guest statistics + if len(args) > 0 : + update = args[0] + else: + update = 1 + if guest.statisticsUpdateInterval != update: + guest.statisticsUpdateInterval = update + try: + time.sleep(float(update)+0.1) + except: + # to allow sleep interruption + pass + all_stats = ctx['const'].all_values('GuestStatisticType') + cpu = 0 + for s in list(all_stats.keys()): + try: + val = guest.getStatistic( cpu, all_stats[s]) + print("%s: %d" % (s, val)) + except: + # likely not implemented + pass + +def plugCpu(_ctx, machine, _session, args): + cpu = int(args[0]) + print("Adding CPU %d..." % (cpu)) + machine.hotPlugCPU(cpu) + +def unplugCpu(_ctx, machine, _session, args): + cpu = int(args[0]) + print("Removing CPU %d..." % (cpu)) + machine.hotUnplugCPU(cpu) + +def mountIso(_ctx, machine, _session, args): + machine.mountMedium(args[0], args[1], args[2], args[3], args[4]) + machine.saveSettings() + +def cond(c, v1, v2): + if c: + return v1 + else: + return v2 + +def printHostUsbDev(ctx, ud): + print(" %s: %s (vendorId=%d productId=%d serial=%s) %s" % (ud.id, colored(ud.product, 'blue'), ud.vendorId, ud.productId, ud.serialNumber, asEnumElem(ctx, 'USBDeviceState', ud.state))) + +def printUsbDev(_ctx, ud): + print(" %s: %s (vendorId=%d productId=%d serial=%s)" % (ud.id, colored(ud.product, 'blue'), ud.vendorId, ud.productId, ud.serialNumber)) + +def printSf(ctx, sf): + print(" name=%s host=%s %s %s" % (sf.name, colPath(ctx, sf.hostPath), cond(sf.accessible, "accessible", "not accessible"), cond(sf.writable, "writable", "read-only"))) + +def ginfo(ctx, console, _args): + guest = console.guest + if guest.additionsRunLevel != ctx['const'].AdditionsRunLevelType_None: + print("Additions active, version %s" % (guest.additionsVersion)) + print("Support seamless: %s" % (getFacilityStatus(ctx, guest, ctx['const'].AdditionsFacilityType_Seamless))) + print("Support graphics: %s" % (getFacilityStatus(ctx, guest, ctx['const'].AdditionsFacilityType_Graphics))) + print("Balloon size: %d" % (guest.memoryBalloonSize)) + print("Statistic update interval: %d" % (guest.statisticsUpdateInterval)) + else: + print("No additions") + usbs = ctx['global'].getArray(console, 'USBDevices') + print("Attached USB:") + for ud in usbs: + printUsbDev(ctx, ud) + rusbs = ctx['global'].getArray(console, 'remoteUSBDevices') + print("Remote USB:") + for ud in rusbs: + printHostUsbDev(ctx, ud) + print("Transient shared folders:") + sfs = rusbs = ctx['global'].getArray(console, 'sharedFolders') + for sf in sfs: + printSf(ctx, sf) + +def cmdExistingVm(ctx, mach, cmd, args): + session = None + try: + vbox = ctx['vb'] + session = ctx['global'].openMachineSession(mach, fPermitSharing=True) + except Exception as e: + printErr(ctx, "Session to '%s' not open: %s" % (mach.name, str(e))) + if g_fVerbose: + traceback.print_exc() + return + if session.state != ctx['const'].SessionState_Locked: + print("Session to '%s' in wrong state: %s" % (mach.name, session.state)) + session.unlockMachine() + return + # this could be an example how to handle local only (i.e. unavailable + # in Webservices) functionality + if ctx['remote'] and cmd == 'some_local_only_command': + print('Trying to use local only functionality, ignored') + session.unlockMachine() + return + console = session.console + ops = {'pause': lambda: console.pause(), + 'resume': lambda: console.resume(), + 'powerdown': lambda: console.powerDown(), + 'powerbutton': lambda: console.powerButton(), + 'stats': lambda: perfStats(ctx, mach), + 'guest': lambda: guestExec(ctx, mach, console, args), + 'ginfo': lambda: ginfo(ctx, console, args), + 'guestlambda': lambda: args[0](ctx, mach, console, args[1:]), + 'save': lambda: progressBar(ctx, session.machine.saveState()), + 'screenshot': lambda: takeScreenshot(ctx, console, args), + 'teleport': lambda: teleport(ctx, session, console, args), + 'gueststats': lambda: guestStats(ctx, console, args), + 'plugcpu': lambda: plugCpu(ctx, session.machine, session, args), + 'unplugcpu': lambda: unplugCpu(ctx, session.machine, session, args), + 'mountiso': lambda: mountIso(ctx, session.machine, session, args), + } + try: + ops[cmd]() + except KeyboardInterrupt: + ctx['interrupt'] = True + except Exception as e: + printErr(ctx, e) + if g_fVerbose: + traceback.print_exc() + + session.unlockMachine() + + +def cmdClosedVm(ctx, mach, cmd, args=[], save=True): + session = ctx['global'].openMachineSession(mach, fPermitSharing=True) + mach = session.machine + try: + cmd(ctx, mach, args) + except Exception as e: + save = False + printErr(ctx, e) + if g_fVerbose: + traceback.print_exc() + if save: + try: + mach.saveSettings() + except Exception as e: + printErr(ctx, e) + if g_fVerbose: + traceback.print_exc() + ctx['global'].closeMachineSession(session) + + +def cmdAnyVm(ctx, mach, cmd, args=[], save=False): + session = ctx['global'].openMachineSession(mach, fPermitSharing=True) + mach = session.machine + try: + cmd(ctx, mach, session.console, args) + except Exception as e: + save = False + printErr(ctx, e) + if g_fVerbose: + traceback.print_exc() + if save: + mach.saveSettings() + ctx['global'].closeMachineSession(session) + +def machById(ctx, uuid): + mach = ctx['vb'].findMachine(uuid) + return mach + +class XPathNode: + def __init__(self, parent, obj, ntype): + self.parent = parent + self.obj = obj + self.ntype = ntype + def lookup(self, subpath): + children = self.enum() + matches = [] + for e in children: + if e.matches(subpath): + matches.append(e) + return matches + def enum(self): + return [] + def matches(self, subexp): + if subexp == self.ntype: + return True + if not subexp.startswith(self.ntype): + return False + match = re.search(r"@(?P<a>\w+)=(?P<v>[^\'\[\]]+)", subexp) + matches = False + try: + if match is not None: + xdict = match.groupdict() + attr = xdict['a'] + val = xdict['v'] + matches = (str(getattr(self.obj, attr)) == val) + except: + pass + return matches + def apply(self, cmd): + exec(cmd, {'obj':self.obj, 'node':self, 'ctx':self.getCtx()}, {}) + def getCtx(self): + if hasattr(self, 'ctx'): + return self.ctx + return self.parent.getCtx() + +class XPathNodeHolder(XPathNode): + def __init__(self, parent, obj, attr, heldClass, xpathname): + XPathNode.__init__(self, parent, obj, 'hld '+xpathname) + self.attr = attr + self.heldClass = heldClass + self.xpathname = xpathname + def enum(self): + children = [] + for node in self.getCtx()['global'].getArray(self.obj, self.attr): + nodexml = self.heldClass(self, node) + children.append(nodexml) + return children + def matches(self, subexp): + return subexp == self.xpathname + +class XPathNodeValue(XPathNode): + def __init__(self, parent, obj, xpathname): + XPathNode.__init__(self, parent, obj, 'val '+xpathname) + self.xpathname = xpathname + def matches(self, subexp): + return subexp == self.xpathname + +class XPathNodeHolderVM(XPathNodeHolder): + def __init__(self, parent, vbox): + XPathNodeHolder.__init__(self, parent, vbox, 'machines', XPathNodeVM, 'vms') + +class XPathNodeVM(XPathNode): + def __init__(self, parent, obj): + XPathNode.__init__(self, parent, obj, 'vm') + #def matches(self, subexp): + # return subexp=='vm' + def enum(self): + return [XPathNodeHolderNIC(self, self.obj), + XPathNodeValue(self, self.obj.BIOSSettings, 'bios'), ] + +class XPathNodeHolderNIC(XPathNodeHolder): + def __init__(self, parent, mach): + XPathNodeHolder.__init__(self, parent, mach, 'nics', XPathNodeVM, 'nics') + self.maxNic = self.getCtx()['vb'].systemProperties.getMaxNetworkAdapters(self.obj.chipsetType) + def enum(self): + children = [] + for i in range(0, self.maxNic): + node = XPathNodeNIC(self, self.obj.getNetworkAdapter(i)) + children.append(node) + return children + +class XPathNodeNIC(XPathNode): + def __init__(self, parent, obj): + XPathNode.__init__(self, parent, obj, 'nic') + def matches(self, subexp): + return subexp == 'nic' + +class XPathNodeRoot(XPathNode): + def __init__(self, ctx): + XPathNode.__init__(self, None, None, 'root') + self.ctx = ctx + def enum(self): + return [XPathNodeHolderVM(self, self.ctx['vb'])] + def matches(self, subexp): + return True + +def eval_xpath(ctx, scope): + pathnames = scope.split("/")[2:] + nodes = [XPathNodeRoot(ctx)] + for path in pathnames: + seen = [] + while len(nodes) > 0: + node = nodes.pop() + seen.append(node) + for s in seen: + matches = s.lookup(path) + for match in matches: + nodes.append(match) + if len(nodes) == 0: + break + return nodes + +def argsToMach(ctx, args): + if len(args) < 2: + print("usage: %s [vmname|uuid]" % (args[0])) + return None + uuid = args[1] + mach = machById(ctx, uuid) + if mach == None: + print("Machine '%s' is unknown, use list command to find available machines" % (uuid)) + return mach + +def helpSingleCmd(cmd, h, sp): + if sp != 0: + spec = " [ext from "+sp+"]" + else: + spec = "" + print(" %s: %s%s" % (colored(cmd, 'blue'), h, spec)) + +def helpCmd(_ctx, args): + if len(args) == 1: + print("Help page:") + names = list(commands.keys()) + names.sort() + for i in names: + helpSingleCmd(i, commands[i][0], commands[i][2]) + else: + cmd = args[1] + c = commands.get(cmd) + if c == None: + print("Command '%s' not known" % (cmd)) + else: + helpSingleCmd(cmd, c[0], c[2]) + return 0 + +def asEnumElem(ctx, enum, elem): + enumVals = ctx['const'].all_values(enum) + for e in list(enumVals.keys()): + if str(elem) == str(enumVals[e]): + return colored(e, 'green') + return colored("<unknown>", 'green') + +def enumFromString(ctx, enum, strg): + enumVals = ctx['const'].all_values(enum) + return enumVals.get(strg, None) + +def listCmd(ctx, _args): + for mach in getMachines(ctx, True): + try: + if mach.teleporterEnabled: + tele = "[T] " + else: + tele = " " + print("%sMachine '%s' [%s], machineState=%s, sessionState=%s" % (tele, colVm(ctx, mach.name), mach.id, asEnumElem(ctx, "MachineState", mach.state), asEnumElem(ctx, "SessionState", mach.sessionState))) + except Exception as e: + printErr(ctx, e) + if g_fVerbose: + traceback.print_exc() + return 0 + +def infoCmd(ctx, args): + if len(args) < 2: + print("usage: info [vmname|uuid]") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + try: + vmos = ctx['vb'].getGuestOSType(mach.OSTypeId) + except: + vmos = None + print(" One can use setvar <mach> <var> <value> to change variable, using name in [].") + print(" Name [name]: %s" % (colVm(ctx, mach.name))) + print(" Description [description]: %s" % (mach.description)) + print(" ID [n/a]: %s" % (mach.id)) + print(" OS Type [via OSTypeId]: %s" % (vmos.description if vmos is not None else mach.OSTypeId)) + print(" Firmware [firmwareType]: %s (%s)" % (asEnumElem(ctx, "FirmwareType", mach.firmwareType), mach.firmwareType)) + print() + print(" CPUs [CPUCount]: %d" % (mach.CPUCount)) + print(" RAM [memorySize]: %dM" % (mach.memorySize)) + print(" VRAM [VRAMSize]: %dM" % (mach.graphicsAdapter.VRAMSize)) + print(" Monitors [monitorCount]: %d" % (mach.graphicsAdapter.monitorCount)) + print(" Chipset [chipsetType]: %s (%s)" % (asEnumElem(ctx, "ChipsetType", mach.chipsetType), mach.chipsetType)) + print() + print(" Clipboard mode [clipboardMode]: %s (%s)" % (asEnumElem(ctx, "ClipboardMode", mach.clipboardMode), mach.clipboardMode)) + print(" Machine status [n/a]: %s (%s)" % (asEnumElem(ctx, "SessionState", mach.sessionState), mach.sessionState)) + print() + if mach.teleporterEnabled: + print(" Teleport target on port %d (%s)" % (mach.teleporterPort, mach.teleporterPassword)) + print() + bios = mach.BIOSSettings + print(" ACPI [BIOSSettings.ACPIEnabled]: %s" % (asState(bios.ACPIEnabled))) + print(" APIC [BIOSSettings.IOAPICEnabled]: %s" % (asState(bios.IOAPICEnabled))) + hwVirtEnabled = mach.getHWVirtExProperty(ctx['global'].constants.HWVirtExPropertyType_Enabled) + print(" Hardware virtualization [guest win machine.setHWVirtExProperty(ctx[\\'const\\'].HWVirtExPropertyType_Enabled, value)]: " + asState(hwVirtEnabled)) + hwVirtVPID = mach.getHWVirtExProperty(ctx['const'].HWVirtExPropertyType_VPID) + print(" VPID support [guest win machine.setHWVirtExProperty(ctx[\\'const\\'].HWVirtExPropertyType_VPID, value)]: " + asState(hwVirtVPID)) + hwVirtNestedPaging = mach.getHWVirtExProperty(ctx['const'].HWVirtExPropertyType_NestedPaging) + print(" Nested paging [guest win machine.setHWVirtExProperty(ctx[\\'const\\'].HWVirtExPropertyType_NestedPaging, value)]: " + asState(hwVirtNestedPaging)) + + print(" Hardware 3d acceleration [accelerate3DEnabled]: " + asState(mach.graphicsAdapter.accelerate3DEnabled)) + print(" Hardware 2d video acceleration [accelerate2DVideoEnabled]: " + asState(mach.graphicsAdapter.accelerate2DVideoEnabled)) + + print(" Use universal time [RTCUseUTC]: %s" % (asState(mach.RTCUseUTC))) + print(" HPET [HPETEnabled]: %s" % (asState(mach.HPETEnabled))) + if mach.audioAdapter.enabled: + print(" Audio [via audioAdapter]: chip %s; host driver %s" % (asEnumElem(ctx, "AudioControllerType", mach.audioAdapter.audioController), asEnumElem(ctx, "AudioDriverType", mach.audioAdapter.audioDriver))) + print(" CPU hotplugging [CPUHotPlugEnabled]: %s" % (asState(mach.CPUHotPlugEnabled))) + + print(" Keyboard [keyboardHIDType]: %s (%s)" % (asEnumElem(ctx, "KeyboardHIDType", mach.keyboardHIDType), mach.keyboardHIDType)) + print(" Pointing device [pointingHIDType]: %s (%s)" % (asEnumElem(ctx, "PointingHIDType", mach.pointingHIDType), mach.pointingHIDType)) + print(" Last changed [n/a]: " + time.asctime(time.localtime(int(mach.lastStateChange)/1000))) + # OSE has no VRDE + try: + print(" VRDE server [VRDEServer.enabled]: %s" % (asState(mach.VRDEServer.enabled))) + except: + pass + + print() + print(colCat(ctx, " USB Controllers:")) + for oUsbCtrl in ctx['global'].getArray(mach, 'USBControllers'): + print(" '%s': type %s standard: %#x" \ + % (oUsbCtrl.name, asEnumElem(ctx, "USBControllerType", oUsbCtrl.type), oUsbCtrl.USBStandard)) + + print() + print(colCat(ctx, " I/O subsystem info:")) + print(" Cache enabled [IOCacheEnabled]: %s" % (asState(mach.IOCacheEnabled))) + print(" Cache size [IOCacheSize]: %dM" % (mach.IOCacheSize)) + + controllers = ctx['global'].getArray(mach, 'storageControllers') + if controllers: + print() + print(colCat(ctx, " Storage Controllers:")) + for controller in controllers: + print(" '%s': bus %s type %s" % (controller.name, asEnumElem(ctx, "StorageBus", controller.bus), asEnumElem(ctx, "StorageControllerType", controller.controllerType))) + + attaches = ctx['global'].getArray(mach, 'mediumAttachments') + if attaches: + print() + print(colCat(ctx, " Media:")) + for a in attaches: + print(" Controller: '%s' port/device: %d:%d type: %s (%s):" % (a.controller, a.port, a.device, asEnumElem(ctx, "DeviceType", a.type), a.type)) + medium = a.medium + if a.type == ctx['global'].constants.DeviceType_HardDisk: + print(" HDD:") + print(" Id: %s" % (medium.id)) + print(" Location: %s" % (colPath(ctx, medium.location))) + print(" Name: %s" % (medium.name)) + print(" Format: %s" % (medium.format)) + + if a.type == ctx['global'].constants.DeviceType_DVD: + print(" DVD:") + if medium: + print(" Id: %s" % (medium.id)) + print(" Name: %s" % (medium.name)) + if medium.hostDrive: + print(" Host DVD %s" % (colPath(ctx, medium.location))) + if a.passthrough: + print(" [passthrough mode]") + else: + print(" Virtual image at %s" % (colPath(ctx, medium.location))) + print(" Size: %s" % (medium.size)) + + if a.type == ctx['global'].constants.DeviceType_Floppy: + print(" Floppy:") + if medium: + print(" Id: %s" % (medium.id)) + print(" Name: %s" % (medium.name)) + if medium.hostDrive: + print(" Host floppy %s" % (colPath(ctx, medium.location))) + else: + print(" Virtual image at %s" % (colPath(ctx, medium.location))) + print(" Size: %s" % (medium.size)) + + print() + print(colCat(ctx, " Shared folders:")) + for sf in ctx['global'].getArray(mach, 'sharedFolders'): + printSf(ctx, sf) + + return 0 + +def startCmd(ctx, args): + if len(args) < 2: + print("usage: start name <frontend>") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + if len(args) > 2: + vmtype = args[2] + else: + vmtype = "gui" + startVm(ctx, mach, vmtype) + return 0 + +def createVmCmd(ctx, args): + if len(args) != 3: + print("usage: createvm name ostype") + return 0 + name = args[1] + oskind = args[2] + try: + ctx['vb'].getGuestOSType(oskind) + except Exception: + print('Unknown OS type:', oskind) + return 0 + createVm(ctx, name, oskind) + return 0 + +def ginfoCmd(ctx, args): + if len(args) < 2: + print("usage: ginfo [vmname|uuid]") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + cmdExistingVm(ctx, mach, 'ginfo', '') + return 0 + +def execInGuest(ctx, console, args, env, user, passwd, tmo, inputPipe=None, outputPipe=None): + if len(args) < 1: + print("exec in guest needs at least program name") + return + guest = console.guest + guestSession = guest.createSession(user, passwd, "", "vboxshell guest exec") + # shall contain program name as argv[0] + gargs = args + print("executing %s with args %s as %s" % (args[0], gargs, user)) + flags = 0 + if inputPipe is not None: + flags = 1 # set WaitForProcessStartOnly + print(args[0]) + process = guestSession.processCreate(args[0], gargs, env, [], tmo) + print("executed with pid %d" % (process.PID)) + if pid != 0: + try: + while True: + if inputPipe is not None: + indata = inputPipe(ctx) + if indata is not None: + write = len(indata) + off = 0 + while write > 0: + w = guest.setProcessInput(pid, 0, 10*1000, indata[off:]) + off = off + w + write = write - w + else: + # EOF + try: + guest.setProcessInput(pid, 1, 10*1000, " ") + except: + pass + data = guest.getProcessOutput(pid, 0, 10000, 4096) + if data and len(data) > 0: + sys.stdout.write(data) + continue + progress.waitForCompletion(100) + ctx['global'].waitForEvents(0) + data = guest.getProcessOutput(pid, 0, 0, 4096) + if data and len(data) > 0: + if outputPipe is not None: + outputPipe(ctx, data) + else: + sys.stdout.write(data) + continue + if progress.completed: + break + + except KeyboardInterrupt: + print("Interrupted.") + ctx['interrupt'] = True + if progress.cancelable: + progress.cancel() + (_reason, code, _flags) = guest.getProcessStatus(pid) + print("Exit code: %d" % (code)) + return 0 + else: + reportError(ctx, progress) + +def copyToGuest(ctx, console, args, user, passwd): + src = args[0] + dst = args[1] + flags = 0 + print("Copying host %s to guest %s" % (src, dst)) + progress = console.guest.copyToGuest(src, dst, user, passwd, flags) + progressBar(ctx, progress) + +def nh_raw_input(prompt=""): + stream = sys.stdout + prompt = str(prompt) + if prompt: + stream.write(prompt) + line = sys.stdin.readline() + if not line: + raise EOFError + if line[-1] == '\n': + line = line[:-1] + return line + + +def getCred(_ctx): + import getpass + user = getpass.getuser() + user_inp = nh_raw_input("User (%s): " % (user)) + if len(user_inp) > 0: + user = user_inp + passwd = getpass.getpass() + + return (user, passwd) + +def gexecCmd(ctx, args): + if len(args) < 2: + print("usage: gexec [vmname|uuid] command args") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + gargs = args[2:] + env = [] # ["DISPLAY=:0"] + (user, passwd) = getCred(ctx) + gargs.insert(0, lambda ctx, mach, console, args: execInGuest(ctx, console, args, env, user, passwd, 10000)) + cmdExistingVm(ctx, mach, 'guestlambda', gargs) + return 0 + +def gcopyCmd(ctx, args): + if len(args) < 2: + print("usage: gcopy [vmname|uuid] host_path guest_path") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + gargs = args[2:] + (user, passwd) = getCred(ctx) + gargs.insert(0, lambda ctx, mach, console, args: copyToGuest(ctx, console, args, user, passwd)) + cmdExistingVm(ctx, mach, 'guestlambda', gargs) + return 0 + +def readCmdPipe(ctx, _hcmd): + try: + return ctx['process'].communicate()[0] + except: + return None + +def gpipeCmd(ctx, args): + if len(args) < 4: + print("usage: gpipe [vmname|uuid] hostProgram guestProgram, such as gpipe linux '/bin/uname -a' '/bin/sh -c \"/usr/bin/tee; /bin/uname -a\"'") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + hcmd = args[2] + gcmd = args[3] + (user, passwd) = getCred(ctx) + import subprocess + ctx['process'] = subprocess.Popen(split_no_quotes(hcmd), stdout=subprocess.PIPE) + gargs = split_no_quotes(gcmd) + env = [] + gargs.insert(0, lambda ctx, mach, console, args: execInGuest(ctx, console, args, env, user, passwd, 10000, lambda ctx:readCmdPipe(ctx, hcmd))) + cmdExistingVm(ctx, mach, 'guestlambda', gargs) + try: + ctx['process'].terminate() + except: + pass + ctx['process'] = None + return 0 + + +def removeVmCmd(ctx, args): + mach = argsToMach(ctx, args) + if mach == None: + return 0 + removeVm(ctx, mach) + return 0 + +def pauseCmd(ctx, args): + mach = argsToMach(ctx, args) + if mach == None: + return 0 + cmdExistingVm(ctx, mach, 'pause', '') + return 0 + +def powerdownCmd(ctx, args): + mach = argsToMach(ctx, args) + if mach == None: + return 0 + cmdExistingVm(ctx, mach, 'powerdown', '') + return 0 + +def powerbuttonCmd(ctx, args): + mach = argsToMach(ctx, args) + if mach == None: + return 0 + cmdExistingVm(ctx, mach, 'powerbutton', '') + return 0 + +def resumeCmd(ctx, args): + mach = argsToMach(ctx, args) + if mach == None: + return 0 + cmdExistingVm(ctx, mach, 'resume', '') + return 0 + +def saveCmd(ctx, args): + mach = argsToMach(ctx, args) + if mach == None: + return 0 + cmdExistingVm(ctx, mach, 'save', '') + return 0 + +def statsCmd(ctx, args): + mach = argsToMach(ctx, args) + if mach == None: + return 0 + cmdExistingVm(ctx, mach, 'stats', '') + return 0 + +def guestCmd(ctx, args): + if len(args) < 3: + print("usage: guest name commands") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + if mach.state != ctx['const'].MachineState_Running: + cmdClosedVm(ctx, mach, lambda ctx, mach, a: guestExec (ctx, mach, None, ' '.join(args[2:]))) + else: + cmdExistingVm(ctx, mach, 'guest', ' '.join(args[2:])) + return 0 + +def screenshotCmd(ctx, args): + if len(args) < 2: + print("usage: screenshot vm <file> <width> <height> <monitor>") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + cmdExistingVm(ctx, mach, 'screenshot', args[2:]) + return 0 + +def teleportCmd(ctx, args): + if len(args) < 3: + print("usage: teleport name host:port <password>") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + cmdExistingVm(ctx, mach, 'teleport', args[2:]) + return 0 + +def portalsettings(_ctx, mach, args): + enabled = args[0] + mach.teleporterEnabled = enabled + if enabled: + port = args[1] + passwd = args[2] + mach.teleporterPort = port + mach.teleporterPassword = passwd + +def openportalCmd(ctx, args): + if len(args) < 3: + print("usage: openportal name port <password>") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + port = int(args[2]) + if len(args) > 3: + passwd = args[3] + else: + passwd = "" + if not mach.teleporterEnabled or mach.teleporterPort != port or passwd: + cmdClosedVm(ctx, mach, portalsettings, [True, port, passwd]) + startVm(ctx, mach, "gui") + return 0 + +def closeportalCmd(ctx, args): + if len(args) < 2: + print("usage: closeportal name") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + if mach.teleporterEnabled: + cmdClosedVm(ctx, mach, portalsettings, [False]) + return 0 + +def gueststatsCmd(ctx, args): + if len(args) < 2: + print("usage: gueststats name <check interval>") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + cmdExistingVm(ctx, mach, 'gueststats', args[2:]) + return 0 + +def plugcpu(_ctx, mach, args): + plug = args[0] + cpu = args[1] + if plug: + print("Adding CPU %d..." % (cpu)) + mach.hotPlugCPU(cpu) + else: + print("Removing CPU %d..." % (cpu)) + mach.hotUnplugCPU(cpu) + +def plugcpuCmd(ctx, args): + if len(args) < 2: + print("usage: plugcpu name cpuid") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + if str(mach.sessionState) != str(ctx['const'].SessionState_Locked): + if mach.CPUHotPlugEnabled: + cmdClosedVm(ctx, mach, plugcpu, [True, int(args[2])]) + else: + cmdExistingVm(ctx, mach, 'plugcpu', args[2]) + return 0 + +def unplugcpuCmd(ctx, args): + if len(args) < 2: + print("usage: unplugcpu name cpuid") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + if str(mach.sessionState) != str(ctx['const'].SessionState_Locked): + if mach.CPUHotPlugEnabled: + cmdClosedVm(ctx, mach, plugcpu, [False, int(args[2])]) + else: + cmdExistingVm(ctx, mach, 'unplugcpu', args[2]) + return 0 + +def setvar(_ctx, _mach, args): + expr = 'mach.'+args[0]+' = '+args[1] + print("Executing", expr) + exec(expr) + +def setvarCmd(ctx, args): + if len(args) < 4: + print("usage: setvar [vmname|uuid] expr value") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + cmdClosedVm(ctx, mach, setvar, args[2:]) + return 0 + +def setvmextra(_ctx, mach, args): + key = args[0] + value = args[1] + print("%s: setting %s to %s" % (mach.name, key, value if value else None)) + mach.setExtraData(key, value) + +def setExtraDataCmd(ctx, args): + if len(args) < 3: + print("usage: setextra [vmname|uuid|global] key <value>") + return 0 + key = args[2] + if len(args) == 4: + value = args[3] + else: + value = '' + if args[1] == 'global': + ctx['vb'].setExtraData(key, value) + return 0 + + mach = argsToMach(ctx, args) + if mach == None: + return 0 + cmdClosedVm(ctx, mach, setvmextra, [key, value]) + return 0 + +def printExtraKey(obj, key, value): + print("%s: '%s' = '%s'" % (obj, key, value)) + +def getExtraDataCmd(ctx, args): + if len(args) < 2: + print("usage: getextra [vmname|uuid|global] <key>") + return 0 + if len(args) == 3: + key = args[2] + else: + key = None + + if args[1] == 'global': + obj = ctx['vb'] + else: + obj = argsToMach(ctx, args) + if obj == None: + return 0 + + if key == None: + keys = obj.getExtraDataKeys() + else: + keys = [ key ] + for k in keys: + printExtraKey(args[1], k, obj.getExtraData(k)) + + return 0 + +def quitCmd(_ctx, _args): + return 1 + +def aliasCmd(ctx, args): + if len(args) == 3: + aliases[args[1]] = args[2] + return 0 + + for (key, value) in list(aliases.items()): + print("'%s' is an alias for '%s'" % (key, value)) + return 0 + +def verboseCmd(ctx, args): + global g_fVerbose + if len(args) > 1: + g_fVerbose = (args[1]=='on') + else: + g_fVerbose = not g_fVerbose + return 0 + +def colorsCmd(ctx, args): + global g_fHasColors + if len(args) > 1: + g_fHasColors = (args[1] == 'on') + else: + g_fHasColors = not g_fHasColors + return 0 + +def hostCmd(ctx, args): + vbox = ctx['vb'] + try: + print("VirtualBox version %s" % (colored(vbox.version, 'blue'))) + except Exception as e: + printErr(ctx, e) + if g_fVerbose: + traceback.print_exc() + props = vbox.systemProperties + print("Machines: %s" % (colPath(ctx, props.defaultMachineFolder))) + + #print("Global shared folders:") + #for ud in ctx['global'].getArray(vbox, 'sharedFolders'): + # printSf(ctx, sf) + host = vbox.host + cnt = host.processorCount + print(colCat(ctx, "Processors:")) + print(" available/online: %d/%d " % (cnt, host.processorOnlineCount)) + for i in range(0, cnt): + print(" processor #%d speed: %dMHz %s" % (i, host.getProcessorSpeed(i), host.getProcessorDescription(i))) + + print(colCat(ctx, "RAM:")) + print(" %dM (free %dM)" % (host.memorySize, host.memoryAvailable)) + print(colCat(ctx, "OS:")) + print(" %s (%s)" % (host.operatingSystem, host.OSVersion)) + if host.acceleration3DAvailable: + print(colCat(ctx, "3D acceleration available")) + else: + print(colCat(ctx, "3D acceleration NOT available")) + + print(colCat(ctx, "Network interfaces:")) + for ni in ctx['global'].getArray(host, 'networkInterfaces'): + print(" %s (%s)" % (ni.name, ni.IPAddress)) + + print(colCat(ctx, "DVD drives:")) + for dd in ctx['global'].getArray(host, 'DVDDrives'): + print(" %s - %s" % (dd.name, dd.description)) + + print(colCat(ctx, "Floppy drives:")) + for dd in ctx['global'].getArray(host, 'floppyDrives'): + print(" %s - %s" % (dd.name, dd.description)) + + print(colCat(ctx, "USB devices:")) + for ud in ctx['global'].getArray(host, 'USBDevices'): + printHostUsbDev(ctx, ud) + + if ctx['perf']: + for metric in ctx['perf'].query(["*"], [host]): + print(metric['name'], metric['values_as_string']) + + return 0 + +def monitorGuestCmd(ctx, args): + if len(args) < 2: + print("usage: monitorGuest name (duration)") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + dur = 5 + if len(args) > 2: + dur = float(args[2]) + active = False + cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.eventSource, active, dur)]) + return 0 + +def monitorGuestKbdCmd(ctx, args): + if len(args) < 2: + print("usage: monitorGuestKbd name (duration)") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + dur = 5 + if len(args) > 2: + dur = float(args[2]) + active = False + cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.keyboard.eventSource, active, dur)]) + return 0 + +def monitorGuestMouseCmd(ctx, args): + if len(args) < 2: + print("usage: monitorGuestMouse name (duration)") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + dur = 5 + if len(args) > 2: + dur = float(args[2]) + active = False + cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.mouse.eventSource, active, dur)]) + return 0 + +def monitorGuestMultiTouchCmd(ctx, args): + if len(args) < 2: + print("usage: monitorGuestMultiTouch name (duration)") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + dur = 5 + if len(args) > 2: + dur = float(args[2]) + active = False + cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.mouse.eventSource, active, dur)]) + return 0 + +def monitorVBoxCmd(ctx, args): + if len(args) > 2: + print("usage: monitorVBox (duration)") + return 0 + dur = 5 + if len(args) > 1: + dur = float(args[1]) + vbox = ctx['vb'] + active = False + monitorSource(ctx, vbox.eventSource, active, dur) + return 0 + +def getAdapterType(ctx, natype): + if (natype == ctx['global'].constants.NetworkAdapterType_Am79C970A or + natype == ctx['global'].constants.NetworkAdapterType_Am79C973 or + natype == ctx['global'].constants.NetworkAdapterType_Am79C960): + return "pcnet" + elif (natype == ctx['global'].constants.NetworkAdapterType_I82540EM or + natype == ctx['global'].constants.NetworkAdapterType_I82545EM or + natype == ctx['global'].constants.NetworkAdapterType_I82543GC): + return "e1000" + elif (natype == ctx['global'].constants.NetworkAdapterType_Virtio): + return "virtio" + elif (natype == ctx['global'].constants.NetworkAdapterType_Null): + return None + else: + raise Exception("Unknown adapter type: "+natype) + + +def portForwardCmd(ctx, args): + if len(args) != 5: + print("usage: portForward <vm> <adapter> <hostPort> <guestPort>") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + adapterNum = int(args[2]) + hostPort = int(args[3]) + guestPort = int(args[4]) + proto = "TCP" + session = ctx['global'].openMachineSession(mach, fPermitSharing=True) + mach = session.machine + + adapter = mach.getNetworkAdapter(adapterNum) + adapterType = getAdapterType(ctx, adapter.adapterType) + + profile_name = proto+"_"+str(hostPort)+"_"+str(guestPort) + config = "VBoxInternal/Devices/" + adapterType + "/" + config = config + str(adapter.slot) +"/LUN#0/Config/" + profile_name + + mach.setExtraData(config + "/Protocol", proto) + mach.setExtraData(config + "/HostPort", str(hostPort)) + mach.setExtraData(config + "/GuestPort", str(guestPort)) + + mach.saveSettings() + session.unlockMachine() + + return 0 + + +def showLogCmd(ctx, args): + if len(args) < 2: + print("usage: showLog vm <num>") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + + log = 0 + if len(args) > 2: + log = args[2] + + uOffset = 0 + while True: + data = mach.readLog(log, uOffset, 4096) + if len(data) == 0: + break + # print adds either NL or space to chunks not ending with a NL + sys.stdout.write(str(data)) + uOffset += len(data) + + return 0 + +def findLogCmd(ctx, args): + if len(args) < 3: + print("usage: findLog vm pattern <num>") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + + log = 0 + if len(args) > 3: + log = args[3] + + pattern = args[2] + uOffset = 0 + while True: + # to reduce line splits on buffer boundary + data = mach.readLog(log, uOffset, 512*1024) + if len(data) == 0: + break + d = str(data).split("\n") + for s in d: + match = re.findall(pattern, s) + if len(match) > 0: + for mt in match: + s = s.replace(mt, colored(mt, 'red')) + print(s) + uOffset += len(data) + + return 0 + + +def findAssertCmd(ctx, args): + if len(args) < 2: + print("usage: findAssert vm <num>") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + + log = 0 + if len(args) > 2: + log = args[2] + + uOffset = 0 + ere = re.compile(r'(Expression:|\!\!\!\!\!\!)') + active = False + context = 0 + while True: + # to reduce line splits on buffer boundary + data = mach.readLog(log, uOffset, 512*1024) + if len(data) == 0: + break + d = str(data).split("\n") + for s in d: + if active: + print(s) + if context == 0: + active = False + else: + context = context - 1 + continue + match = ere.findall(s) + if len(match) > 0: + active = True + context = 50 + print(s) + uOffset += len(data) + + return 0 + +def evalCmd(ctx, args): + expr = ' '.join(args[1:]) + try: + exec(expr) + except Exception as e: + printErr(ctx, e) + if g_fVerbose: + traceback.print_exc() + return 0 + +def reloadExtCmd(ctx, args): + # maybe will want more args smartness + checkUserExtensions(ctx, commands, getHomeFolder(ctx)) + autoCompletion(commands, ctx) + return 0 + +def runScriptCmd(ctx, args): + if len(args) != 2: + print("usage: runScript <script>") + return 0 + try: + lf = open(args[1], 'r') + except IOError as e: + print("cannot open:", args[1], ":", e) + return 0 + + try: + lines = lf.readlines() + ctx['scriptLine'] = 0 + ctx['interrupt'] = False + while ctx['scriptLine'] < len(lines): + line = lines[ctx['scriptLine']] + ctx['scriptLine'] = ctx['scriptLine'] + 1 + done = runCommand(ctx, line) + if done != 0 or ctx['interrupt']: + break + + except Exception as e: + printErr(ctx, e) + if g_fVerbose: + traceback.print_exc() + lf.close() + return 0 + +def sleepCmd(ctx, args): + if len(args) != 2: + print("usage: sleep <secs>") + return 0 + + try: + time.sleep(float(args[1])) + except: + # to allow sleep interrupt + pass + return 0 + + +def shellCmd(ctx, args): + if len(args) < 2: + print("usage: shell <commands>") + return 0 + cmd = ' '.join(args[1:]) + + try: + os.system(cmd) + except KeyboardInterrupt: + # to allow shell command interruption + pass + return 0 + + +def connectCmd(ctx, args): + if len(args) > 4: + print("usage: connect url <username> <passwd>") + return 0 + + if ctx['vb'] is not None: + print("Already connected, disconnect first...") + return 0 + + if len(args) > 1: + url = args[1] + else: + url = None + + if len(args) > 2: + user = args[2] + else: + user = "" + + if len(args) > 3: + passwd = args[3] + else: + passwd = "" + + ctx['wsinfo'] = [url, user, passwd] + ctx['vb'] = ctx['global'].platform.connect(url, user, passwd) + try: + print("Running VirtualBox version %s" % (ctx['vb'].version)) + except Exception as e: + printErr(ctx, e) + if g_fVerbose: + traceback.print_exc() + ctx['perf'] = ctx['global'].getPerfCollector(ctx['vb']) + return 0 + +def disconnectCmd(ctx, args): + if len(args) != 1: + print("usage: disconnect") + return 0 + + if ctx['vb'] is None: + print("Not connected yet.") + return 0 + + try: + ctx['global'].platform.disconnect() + except: + ctx['vb'] = None + raise + + ctx['vb'] = None + return 0 + +def reconnectCmd(ctx, args): + if ctx['wsinfo'] is None: + print("Never connected...") + return 0 + + try: + ctx['global'].platform.disconnect() + except: + pass + + [url, user, passwd] = ctx['wsinfo'] + ctx['vb'] = ctx['global'].platform.connect(url, user, passwd) + try: + print("Running VirtualBox version %s" % (ctx['vb'].version)) + except Exception as e: + printErr(ctx, e) + if g_fVerbose: + traceback.print_exc() + ctx['perf'] = ctx['global'].getPerfCollector(ctx['vb']) + return 0 + +def exportVMCmd(ctx, args): + if len(args) < 3: + print("usage: exportVm <machine> <path> <format> <license>") + return 0 + mach = argsToMach(ctx, args) + if mach is None: + return 0 + path = args[2] + if len(args) > 3: + fmt = args[3] + else: + fmt = "ovf-1.0" + if len(args) > 4: + lic = args[4] + else: + lic = "GPL" + + app = ctx['vb'].createAppliance() + desc = mach.export(app) + desc.addDescription(ctx['global'].constants.VirtualSystemDescriptionType_License, lic, "") + progress = app.write(fmt, path) + if (progressBar(ctx, progress) and int(progress.resultCode) == 0): + print("Exported to %s in format %s" % (path, fmt)) + else: + reportError(ctx, progress) + return 0 + +# PC XT scancodes +scancodes = { + 'a': 0x1e, + 'b': 0x30, + 'c': 0x2e, + 'd': 0x20, + 'e': 0x12, + 'f': 0x21, + 'g': 0x22, + 'h': 0x23, + 'i': 0x17, + 'j': 0x24, + 'k': 0x25, + 'l': 0x26, + 'm': 0x32, + 'n': 0x31, + 'o': 0x18, + 'p': 0x19, + 'q': 0x10, + 'r': 0x13, + 's': 0x1f, + 't': 0x14, + 'u': 0x16, + 'v': 0x2f, + 'w': 0x11, + 'x': 0x2d, + 'y': 0x15, + 'z': 0x2c, + '0': 0x0b, + '1': 0x02, + '2': 0x03, + '3': 0x04, + '4': 0x05, + '5': 0x06, + '6': 0x07, + '7': 0x08, + '8': 0x09, + '9': 0x0a, + ' ': 0x39, + '-': 0xc, + '=': 0xd, + '[': 0x1a, + ']': 0x1b, + ';': 0x27, + '\'': 0x28, + ',': 0x33, + '.': 0x34, + '/': 0x35, + '\t': 0xf, + '\n': 0x1c, + '`': 0x29 +} + +extScancodes = { + 'ESC' : [0x01], + 'BKSP': [0xe], + 'SPACE': [0x39], + 'TAB': [0x0f], + 'CAPS': [0x3a], + 'ENTER': [0x1c], + 'LSHIFT': [0x2a], + 'RSHIFT': [0x36], + 'INS': [0xe0, 0x52], + 'DEL': [0xe0, 0x53], + 'END': [0xe0, 0x4f], + 'HOME': [0xe0, 0x47], + 'PGUP': [0xe0, 0x49], + 'PGDOWN': [0xe0, 0x51], + 'LGUI': [0xe0, 0x5b], # GUI, aka Win, aka Apple key + 'RGUI': [0xe0, 0x5c], + 'LCTR': [0x1d], + 'RCTR': [0xe0, 0x1d], + 'LALT': [0x38], + 'RALT': [0xe0, 0x38], + 'APPS': [0xe0, 0x5d], + 'F1': [0x3b], + 'F2': [0x3c], + 'F3': [0x3d], + 'F4': [0x3e], + 'F5': [0x3f], + 'F6': [0x40], + 'F7': [0x41], + 'F8': [0x42], + 'F9': [0x43], + 'F10': [0x44 ], + 'F11': [0x57], + 'F12': [0x58], + 'UP': [0xe0, 0x48], + 'LEFT': [0xe0, 0x4b], + 'DOWN': [0xe0, 0x50], + 'RIGHT': [0xe0, 0x4d], +} + +def keyDown(ch): + code = scancodes.get(ch, 0x0) + if code != 0: + return [code] + extCode = extScancodes.get(ch, []) + if len(extCode) == 0: + print("bad ext", ch) + return extCode + +def keyUp(ch): + codes = keyDown(ch)[:] # make a copy + if len(codes) > 0: + codes[len(codes)-1] += 0x80 + return codes + +def typeInGuest(console, text, delay): + pressed = [] + group = False + modGroupEnd = True + i = 0 + kbd = console.keyboard + while i < len(text): + ch = text[i] + i = i+1 + if ch == '{': + # start group, all keys to be pressed at the same time + group = True + continue + if ch == '}': + # end group, release all keys + for c in pressed: + kbd.putScancodes(keyUp(c)) + pressed = [] + group = False + continue + if ch == 'W': + # just wait a bit + time.sleep(0.3) + continue + if ch == '^' or ch == '|' or ch == '$' or ch == '_': + if ch == '^': + ch = 'LCTR' + if ch == '|': + ch = 'LSHIFT' + if ch == '_': + ch = 'LALT' + if ch == '$': + ch = 'LGUI' + if not group: + modGroupEnd = False + else: + if ch == '\\': + if i < len(text): + ch = text[i] + i = i+1 + if ch == 'n': + ch = '\n' + elif ch == '&': + combo = "" + while i < len(text): + ch = text[i] + i = i+1 + if ch == ';': + break + combo += ch + ch = combo + modGroupEnd = True + kbd.putScancodes(keyDown(ch)) + pressed.insert(0, ch) + if not group and modGroupEnd: + for c in pressed: + kbd.putScancodes(keyUp(c)) + pressed = [] + modGroupEnd = True + time.sleep(delay) + +def typeGuestCmd(ctx, args): + if len(args) < 3: + print("usage: typeGuest <machine> <text> <charDelay>") + return 0 + mach = argsToMach(ctx, args) + if mach is None: + return 0 + + text = args[2] + + if len(args) > 3: + delay = float(args[3]) + else: + delay = 0.1 + + gargs = [lambda ctx, mach, console, args: typeInGuest(console, text, delay)] + cmdExistingVm(ctx, mach, 'guestlambda', gargs) + + return 0 + +def optId(verbose, uuid): + if verbose: + return ": "+uuid + else: + return "" + +def asSize(val, inBytes): + if inBytes: + return int(val)/(1024*1024) + else: + return int(val) + +def listMediaCmd(ctx, args): + if len(args) > 1: + verbose = int(args[1]) + else: + verbose = False + hdds = ctx['global'].getArray(ctx['vb'], 'hardDisks') + print(colCat(ctx, "Hard disks:")) + for hdd in hdds: + if hdd.state != ctx['global'].constants.MediumState_Created: + hdd.refreshState() + print(" %s (%s)%s %s [logical %s]" % (colPath(ctx, hdd.location), hdd.format, optId(verbose, hdd.id), colSizeM(ctx, asSize(hdd.size, True)), colSizeM(ctx, asSize(hdd.logicalSize, True)))) + + dvds = ctx['global'].getArray(ctx['vb'], 'DVDImages') + print(colCat(ctx, "CD/DVD disks:")) + for dvd in dvds: + if dvd.state != ctx['global'].constants.MediumState_Created: + dvd.refreshState() + print(" %s (%s)%s %s" % (colPath(ctx, dvd.location), dvd.format, optId(verbose, dvd.id), colSizeM(ctx, asSize(dvd.size, True)))) + + floppys = ctx['global'].getArray(ctx['vb'], 'floppyImages') + print(colCat(ctx, "Floppy disks:")) + for floppy in floppys: + if floppy.state != ctx['global'].constants.MediumState_Created: + floppy.refreshState() + print(" %s (%s)%s %s" % (colPath(ctx, floppy.location), floppy.format, optId(verbose, floppy.id), colSizeM(ctx, asSize(floppy.size, True)))) + + return 0 + +def listUsbCmd(ctx, args): + if len(args) > 1: + print("usage: listUsb") + return 0 + + host = ctx['vb'].host + for ud in ctx['global'].getArray(host, 'USBDevices'): + printHostUsbDev(ctx, ud) + + return 0 + +def findDevOfType(ctx, mach, devtype): + atts = ctx['global'].getArray(mach, 'mediumAttachments') + for a in atts: + if a.type == devtype: + return [a.controller, a.port, a.device] + return [None, 0, 0] + +def createHddCmd(ctx, args): + if len(args) < 3: + print("usage: createHdd sizeM location type") + return 0 + + size = int(args[1]) + loc = args[2] + if len(args) > 3: + fmt = args[3] + else: + fmt = "vdi" + + hdd = ctx['vb'].createMedium(fmt, loc, ctx['global'].constants.AccessMode_ReadWrite, ctx['global'].constants.DeviceType_HardDisk) + progress = hdd.createBaseStorage(size, (ctx['global'].constants.MediumVariant_Standard, )) + if progressBar(ctx,progress) and hdd.id: + print("created HDD at %s as %s" % (colPath(ctx,hdd.location), hdd.id)) + else: + print("cannot create disk (file %s exist?)" % (loc)) + reportError(ctx,progress) + return 0 + + return 0 + +def registerHddCmd(ctx, args): + if len(args) < 2: + print("usage: registerHdd location") + return 0 + + vbox = ctx['vb'] + loc = args[1] + setImageId = False + imageId = "" + setParentId = False + parentId = "" + hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False) + print("registered HDD as %s" % (hdd.id)) + return 0 + +def controldevice(ctx, mach, args): + [ctr, port, slot, devtype, uuid] = args + mach.attachDevice(ctr, port, slot, devtype, uuid) + +def attachHddCmd(ctx, args): + if len(args) < 3: + print("usage: attachHdd vm hdd controller port:slot") + return 0 + + mach = argsToMach(ctx, args) + if mach is None: + return 0 + vbox = ctx['vb'] + loc = args[2] + try: + hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False) + except: + print("no HDD with path %s registered" % (loc)) + return 0 + if len(args) > 3: + ctr = args[3] + (port, slot) = args[4].split(":") + else: + [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_HardDisk) + + cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.attachDevice(ctr, port, slot, ctx['global'].constants.DeviceType_HardDisk, hdd.id)) + return 0 + +def detachVmDevice(ctx, mach, args): + atts = ctx['global'].getArray(mach, 'mediumAttachments') + hid = args[0] + for a in atts: + if a.medium: + if hid == "ALL" or a.medium.id == hid: + mach.detachDevice(a.controller, a.port, a.device) + +def detachMedium(ctx, mid, medium): + cmdClosedVm(ctx, machById(ctx, mid), detachVmDevice, [medium]) + +def detachHddCmd(ctx, args): + if len(args) < 3: + print("usage: detachHdd vm hdd") + return 0 + + mach = argsToMach(ctx, args) + if mach is None: + return 0 + vbox = ctx['vb'] + loc = args[2] + try: + hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False) + except: + print("no HDD with path %s registered" % (loc)) + return 0 + + detachMedium(ctx, mach.id, hdd) + return 0 + +def unregisterHddCmd(ctx, args): + if len(args) < 2: + print("usage: unregisterHdd path <vmunreg>") + return 0 + + vbox = ctx['vb'] + loc = args[1] + if len(args) > 2: + vmunreg = int(args[2]) + else: + vmunreg = 0 + try: + hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False) + except: + print("no HDD with path %s registered" % (loc)) + return 0 + + if vmunreg != 0: + machs = ctx['global'].getArray(hdd, 'machineIds') + try: + for mach in machs: + print("Trying to detach from %s" % (mach)) + detachMedium(ctx, mach, hdd) + except Exception as e: + print('failed: ', e) + return 0 + hdd.close() + return 0 + +def removeHddCmd(ctx, args): + if len(args) != 2: + print("usage: removeHdd path") + return 0 + + vbox = ctx['vb'] + loc = args[1] + try: + hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False) + except: + print("no HDD with path %s registered" % (loc)) + return 0 + + progress = hdd.deleteStorage() + progressBar(ctx, progress) + + return 0 + +def registerIsoCmd(ctx, args): + if len(args) < 2: + print("usage: registerIso location") + return 0 + + vbox = ctx['vb'] + loc = args[1] + iso = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False) + print("registered ISO as %s" % (iso.id)) + return 0 + +def unregisterIsoCmd(ctx, args): + if len(args) != 2: + print("usage: unregisterIso path") + return 0 + + vbox = ctx['vb'] + loc = args[1] + try: + dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False) + except: + print("no DVD with path %s registered" % (loc)) + return 0 + + progress = dvd.close() + print("Unregistered ISO at %s" % (colPath(ctx, loc))) + + return 0 + +def removeIsoCmd(ctx, args): + if len(args) != 2: + print("usage: removeIso path") + return 0 + + vbox = ctx['vb'] + loc = args[1] + try: + dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False) + except: + print("no DVD with path %s registered" % (loc)) + return 0 + + progress = dvd.deleteStorage() + if progressBar(ctx, progress): + print("Removed ISO at %s" % (colPath(ctx, dvd.location))) + else: + reportError(ctx, progress) + return 0 + +def attachIsoCmd(ctx, args): + if len(args) < 3: + print("usage: attachIso vm iso controller port:slot") + return 0 + + mach = argsToMach(ctx, args) + if mach is None: + return 0 + vbox = ctx['vb'] + loc = args[2] + try: + dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False) + except: + print("no DVD with path %s registered" % (loc)) + return 0 + if len(args) > 3: + ctr = args[3] + (port, slot) = args[4].split(":") + else: + [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_DVD) + cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.attachDevice(ctr, port, slot, ctx['global'].constants.DeviceType_DVD, dvd)) + return 0 + +def detachIsoCmd(ctx, args): + if len(args) < 3: + print("usage: detachIso vm iso") + return 0 + + mach = argsToMach(ctx, args) + if mach is None: + return 0 + vbox = ctx['vb'] + loc = args[2] + try: + dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False) + except: + print("no DVD with path %s registered" % (loc)) + return 0 + + detachMedium(ctx, mach.id, dvd) + return 0 + +def mountIsoCmd(ctx, args): + if len(args) < 3: + print("usage: mountIso vm iso controller port:slot") + return 0 + + mach = argsToMach(ctx, args) + if mach is None: + return 0 + vbox = ctx['vb'] + loc = args[2] + try: + dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False) + except: + print("no DVD with path %s registered" % (loc)) + return 0 + + if len(args) > 3: + ctr = args[3] + (port, slot) = args[4].split(":") + else: + # autodetect controller and location, just find first controller with media == DVD + [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_DVD) + + cmdExistingVm(ctx, mach, 'mountiso', [ctr, port, slot, dvd, True]) + + return 0 + +def unmountIsoCmd(ctx, args): + if len(args) < 2: + print("usage: unmountIso vm controller port:slot") + return 0 + + mach = argsToMach(ctx, args) + if mach is None: + return 0 + vbox = ctx['vb'] + + if len(args) > 3: + ctr = args[2] + (port, slot) = args[3].split(":") + else: + # autodetect controller and location, just find first controller with media == DVD + [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_DVD) + + cmdExistingVm(ctx, mach, 'mountiso', [ctr, port, slot, None, True]) + + return 0 + +def attachCtr(ctx, mach, args): + [name, bus, ctrltype] = args + ctr = mach.addStorageController(name, bus) + if ctrltype != None: + ctr.controllerType = ctrltype + +def attachCtrCmd(ctx, args): + if len(args) < 4: + print("usage: attachCtr vm cname bus <type>") + return 0 + + if len(args) > 4: + ctrltype = enumFromString(ctx, 'StorageControllerType', args[4]) + if ctrltype == None: + print("Controller type %s unknown" % (args[4])) + return 0 + else: + ctrltype = None + + mach = argsToMach(ctx, args) + if mach is None: + return 0 + bus = enumFromString(ctx, 'StorageBus', args[3]) + if bus is None: + print("Bus type %s unknown" % (args[3])) + return 0 + name = args[2] + cmdClosedVm(ctx, mach, attachCtr, [name, bus, ctrltype]) + return 0 + +def detachCtrCmd(ctx, args): + if len(args) < 3: + print("usage: detachCtr vm name") + return 0 + + mach = argsToMach(ctx, args) + if mach is None: + return 0 + ctr = args[2] + cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.removeStorageController(ctr)) + return 0 + +def usbctr(ctx, mach, console, args): + if args[0]: + console.attachUSBDevice(args[1], "") + else: + console.detachUSBDevice(args[1]) + +def attachUsbCmd(ctx, args): + if len(args) < 3: + print("usage: attachUsb vm deviceuid") + return 0 + + mach = argsToMach(ctx, args) + if mach is None: + return 0 + dev = args[2] + cmdExistingVm(ctx, mach, 'guestlambda', [usbctr, True, dev]) + return 0 + +def detachUsbCmd(ctx, args): + if len(args) < 3: + print("usage: detachUsb vm deviceuid") + return 0 + + mach = argsToMach(ctx, args) + if mach is None: + return 0 + dev = args[2] + cmdExistingVm(ctx, mach, 'guestlambda', [usbctr, False, dev]) + return 0 + + +def guiCmd(ctx, args): + if len(args) > 1: + print("usage: gui") + return 0 + + binDir = ctx['global'].getBinDir() + + vbox = os.path.join(binDir, 'VirtualBox') + try: + os.system(vbox) + except KeyboardInterrupt: + # to allow interruption + pass + return 0 + +def shareFolderCmd(ctx, args): + if len(args) < 4: + print("usage: shareFolder vm path name <writable> <persistent>") + return 0 + + mach = argsToMach(ctx, args) + if mach is None: + return 0 + path = args[2] + name = args[3] + writable = False + persistent = False + if len(args) > 4: + for a in args[4:]: + if a == 'writable': + writable = True + if a == 'persistent': + persistent = True + if persistent: + cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.createSharedFolder(name, path, writable), []) + else: + cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: console.createSharedFolder(name, path, writable)]) + return 0 + +def unshareFolderCmd(ctx, args): + if len(args) < 3: + print("usage: unshareFolder vm name") + return 0 + + mach = argsToMach(ctx, args) + if mach is None: + return 0 + name = args[2] + found = False + for sf in ctx['global'].getArray(mach, 'sharedFolders'): + if sf.name == name: + cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.removeSharedFolder(name), []) + found = True + break + if not found: + cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: console.removeSharedFolder(name)]) + return 0 + + +def snapshotCmd(ctx, args): + if (len(args) < 2 or args[1] == 'help'): + print("Take snapshot: snapshot vm take name <description>") + print("Restore snapshot: snapshot vm restore name") + print("Merge snapshot: snapshot vm merge name") + return 0 + + mach = argsToMach(ctx, args) + if mach is None: + return 0 + cmd = args[2] + if cmd == 'take': + if len(args) < 4: + print("usage: snapshot vm take name <description>") + return 0 + name = args[3] + if len(args) > 4: + desc = args[4] + else: + desc = "" + cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.takeSnapshot(name, desc, True)[0])) + return 0 + + if cmd == 'restore': + if len(args) < 4: + print("usage: snapshot vm restore name") + return 0 + name = args[3] + snap = mach.findSnapshot(name) + cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.restoreSnapshot(snap))) + return 0 + + if cmd == 'restorecurrent': + if len(args) < 4: + print("usage: snapshot vm restorecurrent") + return 0 + snap = mach.currentSnapshot() + cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.restoreSnapshot(snap))) + return 0 + + if cmd == 'delete': + if len(args) < 4: + print("usage: snapshot vm delete name") + return 0 + name = args[3] + snap = mach.findSnapshot(name) + cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.deleteSnapshot(snap.id))) + return 0 + + print("Command '%s' is unknown" % (cmd)) + return 0 + +def natAlias(ctx, mach, nicnum, nat, args=[]): + """This command shows/alters NAT's alias settings. + usage: nat <vm> <nicnum> alias [default|[log] [proxyonly] [sameports]] + default - set settings to default values + log - switch on alias logging + proxyonly - switch proxyonly mode on + sameports - enforces NAT using the same ports + """ + alias = { + 'log': 0x1, + 'proxyonly': 0x2, + 'sameports': 0x4 + } + if len(args) == 1: + first = 0 + msg = '' + for aliasmode, aliaskey in list(alias.items()): + if first == 0: + first = 1 + else: + msg += ', ' + if int(nat.aliasMode) & aliaskey: + msg += '%s: %s' % (aliasmode, 'on') + else: + msg += '%s: %s' % (aliasmode, 'off') + return (0, [msg]) + else: + nat.aliasMode = 0 + if 'default' not in args: + for a in range(1, len(args)): + if args[a] not in alias: + print('Invalid alias mode: ' + args[a]) + print(natAlias.__doc__) + return (1, None) + nat.aliasMode = int(nat.aliasMode) | alias[args[a]] + return (0, None) + +def natSettings(ctx, mach, nicnum, nat, args): + """This command shows/alters NAT settings. + usage: nat <vm> <nicnum> settings [<mtu> [[<socsndbuf> <sockrcvbuf> [<tcpsndwnd> <tcprcvwnd>]]]] + mtu - set mtu <= 16000 + socksndbuf/sockrcvbuf - sets amount of kb for socket sending/receiving buffer + tcpsndwnd/tcprcvwnd - sets size of initial tcp sending/receiving window + """ + if len(args) == 1: + (mtu, socksndbuf, sockrcvbuf, tcpsndwnd, tcprcvwnd) = nat.getNetworkSettings() + if mtu == 0: mtu = 1500 + if socksndbuf == 0: socksndbuf = 64 + if sockrcvbuf == 0: sockrcvbuf = 64 + if tcpsndwnd == 0: tcpsndwnd = 64 + if tcprcvwnd == 0: tcprcvwnd = 64 + msg = 'mtu:%s socket(snd:%s, rcv:%s) tcpwnd(snd:%s, rcv:%s)' % (mtu, socksndbuf, sockrcvbuf, tcpsndwnd, tcprcvwnd) + return (0, [msg]) + else: + if args[1] < 16000: + print('invalid mtu value (%s not in range [65 - 16000])' % (args[1])) + return (1, None) + for i in range(2, len(args)): + if not args[i].isdigit() or int(args[i]) < 8 or int(args[i]) > 1024: + print('invalid %s parameter (%i not in range [8-1024])' % (i, args[i])) + return (1, None) + a = [args[1]] + if len(args) < 6: + for i in range(2, len(args)): a.append(args[i]) + for i in range(len(args), 6): a.append(0) + else: + for i in range(2, len(args)): a.append(args[i]) + #print(a) + nat.setNetworkSettings(int(a[0]), int(a[1]), int(a[2]), int(a[3]), int(a[4])) + return (0, None) + +def natDns(ctx, mach, nicnum, nat, args): + """This command shows/alters DNS's NAT settings + usage: nat <vm> <nicnum> dns [passdomain] [proxy] [usehostresolver] + passdomain - enforces builtin DHCP server to pass domain + proxy - switch on builtin NAT DNS proxying mechanism + usehostresolver - proxies all DNS requests to Host Resolver interface + """ + yesno = {0: 'off', 1: 'on'} + if len(args) == 1: + msg = 'passdomain:%s, proxy:%s, usehostresolver:%s' % (yesno[int(nat.DNSPassDomain)], yesno[int(nat.DNSProxy)], yesno[int(nat.DNSUseHostResolver)]) + return (0, [msg]) + else: + nat.DNSPassDomain = 'passdomain' in args + nat.DNSProxy = 'proxy' in args + nat.DNSUseHostResolver = 'usehostresolver' in args + return (0, None) + +def natTftp(ctx, mach, nicnum, nat, args): + """This command shows/alters TFTP settings + usage nat <vm> <nicnum> tftp [prefix <prefix>| bootfile <bootfile>| server <server>] + prefix - alters prefix TFTP settings + bootfile - alters bootfile TFTP settings + server - sets booting server + """ + if len(args) == 1: + server = nat.TFTPNextServer + if server is None: + server = nat.network + if server is None: + server = '10.0.%d/24' % (int(nicnum) + 2) + (server, mask) = server.split('/') + while server.count('.') != 3: + server += '.0' + (a, b, c, d) = server.split('.') + server = '%d.%d.%d.4' % (a, b, c) + prefix = nat.TFTPPrefix + if prefix is None: + prefix = '%s/TFTP/' % (ctx['vb'].homeFolder) + bootfile = nat.TFTPBootFile + if bootfile is None: + bootfile = '%s.pxe' % (mach.name) + msg = 'server:%s, prefix:%s, bootfile:%s' % (server, prefix, bootfile) + return (0, [msg]) + else: + + cmd = args[1] + if len(args) != 3: + print('invalid args:', args) + print(natTftp.__doc__) + return (1, None) + if cmd == 'prefix': nat.TFTPPrefix = args[2] + elif cmd == 'bootfile': nat.TFTPBootFile = args[2] + elif cmd == 'server': nat.TFTPNextServer = args[2] + else: + print("invalid cmd:", cmd) + return (1, None) + return (0, None) + +def natPortForwarding(ctx, mach, nicnum, nat, args): + """This command shows/manages port-forwarding settings + usage: + nat <vm> <nicnum> <pf> [ simple tcp|udp <hostport> <guestport>] + |[no_name tcp|udp <hostip> <hostport> <guestip> <guestport>] + |[ex tcp|udp <pf-name> <hostip> <hostport> <guestip> <guestport>] + |[delete <pf-name>] + """ + if len(args) == 1: + # note: keys/values are swapped in defining part of the function + proto = {0: 'udp', 1: 'tcp'} + msg = [] + pfs = ctx['global'].getArray(nat, 'redirects') + for pf in pfs: + (pfnme, pfp, pfhip, pfhp, pfgip, pfgp) = str(pf).split(', ') + msg.append('%s: %s %s:%s => %s:%s' % (pfnme, proto[int(pfp)], pfhip, pfhp, pfgip, pfgp)) + return (0, msg) # msg is array + else: + proto = {'udp': 0, 'tcp': 1} + pfcmd = { + 'simple': { + 'validate': lambda: args[1] in list(pfcmd.keys()) and args[2] in list(proto.keys()) and len(args) == 5, + 'func':lambda: nat.addRedirect('', proto[args[2]], '', int(args[3]), '', int(args[4])) + }, + 'no_name': { + 'validate': lambda: args[1] in list(pfcmd.keys()) and args[2] in list(proto.keys()) and len(args) == 7, + 'func': lambda: nat.addRedirect('', proto[args[2]], args[3], int(args[4]), args[5], int(args[6])) + }, + 'ex': { + 'validate': lambda: args[1] in list(pfcmd.keys()) and args[2] in list(proto.keys()) and len(args) == 8, + 'func': lambda: nat.addRedirect(args[3], proto[args[2]], args[4], int(args[5]), args[6], int(args[7])) + }, + 'delete': { + 'validate': lambda: len(args) == 3, + 'func': lambda: nat.removeRedirect(args[2]) + } + } + + if not pfcmd[args[1]]['validate'](): + print('invalid port-forwarding or args of sub command ', args[1]) + print(natPortForwarding.__doc__) + return (1, None) + + a = pfcmd[args[1]]['func']() + return (0, None) + +def natNetwork(ctx, mach, nicnum, nat, args): + """This command shows/alters NAT network settings + usage: nat <vm> <nicnum> network [<network>] + """ + if len(args) == 1: + if nat.network is not None and len(str(nat.network)) != 0: + msg = '\'%s\'' % (nat.network) + else: + msg = '10.0.%d.0/24' % (int(nicnum) + 2) + return (0, [msg]) + else: + (addr, mask) = args[1].split('/') + if addr.count('.') > 3 or int(mask) < 0 or int(mask) > 32: + print('Invalid arguments') + return (1, None) + nat.network = args[1] + return (0, None) + +def natCmd(ctx, args): + """This command is entry point to NAT settins management + usage: nat <vm> <nicnum> <cmd> <cmd-args> + cmd - [alias|settings|tftp|dns|pf|network] + for more information about commands: + nat help <cmd> + """ + + natcommands = { + 'alias' : natAlias, + 'settings' : natSettings, + 'tftp': natTftp, + 'dns': natDns, + 'pf': natPortForwarding, + 'network': natNetwork + } + + if len(args) < 2 or args[1] == 'help': + if len(args) > 2: + print(natcommands[args[2]].__doc__) + else: + print(natCmd.__doc__) + return 0 + if len(args) == 1 or len(args) < 4 or args[3] not in natcommands: + print(natCmd.__doc__) + return 0 + mach = ctx['argsToMach'](args) + if mach == None: + print("please specify vm") + return 0 + if len(args) < 3 or not args[2].isdigit() or int(args[2]) not in list(range(0, ctx['vb'].systemProperties.getMaxNetworkAdapters(mach.chipsetType))): + print('please specify adapter num %d isn\'t in range [0-%d]' % (args[2], ctx['vb'].systemProperties.getMaxNetworkAdapters(mach.chipsetType))) + return 0 + nicnum = int(args[2]) + cmdargs = [] + for i in range(3, len(args)): + cmdargs.append(args[i]) + + # @todo vvl if nicnum is missed but command is entered + # use NAT func for every adapter on machine. + func = args[3] + rosession = 1 + session = None + if len(cmdargs) > 1: + rosession = 0 + session = ctx['global'].openMachineSession(mach, fPermitSharing=False) + mach = session.machine + + adapter = mach.getNetworkAdapter(nicnum) + natEngine = adapter.NATEngine + (rc, report) = natcommands[func](ctx, mach, nicnum, natEngine, cmdargs) + if rosession == 0: + if rc == 0: + mach.saveSettings() + session.unlockMachine() + elif report is not None: + for r in report: + msg ='%s nic%d %s: %s' % (mach.name, nicnum, func, r) + print(msg) + return 0 + +def nicSwitchOnOff(adapter, attr, args): + if len(args) == 1: + yesno = {0: 'off', 1: 'on'} + r = yesno[int(adapter.__getattr__(attr))] + return (0, r) + else: + yesno = {'off' : 0, 'on' : 1} + if args[1] not in yesno: + print('%s isn\'t acceptable, please choose %s' % (args[1], list(yesno.keys()))) + return (1, None) + adapter.__setattr__(attr, yesno[args[1]]) + return (0, None) + +def nicTraceSubCmd(ctx, vm, nicnum, adapter, args): + ''' + usage: nic <vm> <nicnum> trace [on|off [file]] + ''' + (rc, r) = nicSwitchOnOff(adapter, 'traceEnabled', args) + if len(args) == 1 and rc == 0: + r = '%s file:%s' % (r, adapter.traceFile) + return (0, r) + elif len(args) == 3 and rc == 0: + adapter.traceFile = args[2] + return (0, None) + +def nicLineSpeedSubCmd(ctx, vm, nicnum, adapter, args): + if len(args) == 1: + r = '%d kbps'% (adapter.lineSpeed) + return (0, r) + else: + if not args[1].isdigit(): + print('%s isn\'t a number' % (args[1])) + return (1, None) + adapter.lineSpeed = int(args[1]) + return (0, None) + +def nicCableSubCmd(ctx, vm, nicnum, adapter, args): + ''' + usage: nic <vm> <nicnum> cable [on|off] + ''' + return nicSwitchOnOff(adapter, 'cableConnected', args) + +def nicEnableSubCmd(ctx, vm, nicnum, adapter, args): + ''' + usage: nic <vm> <nicnum> enable [on|off] + ''' + return nicSwitchOnOff(adapter, 'enabled', args) + +def nicTypeSubCmd(ctx, vm, nicnum, adapter, args): + ''' + usage: nic <vm> <nicnum> type [Am79c970A|Am79c970A|I82540EM|I82545EM|I82543GC|Virtio] + ''' + if len(args) == 1: + nictypes = ctx['const'].all_values('NetworkAdapterType') + for key in list(nictypes.keys()): + if str(adapter.adapterType) == str(nictypes[key]): + return (0, str(key)) + return (1, None) + else: + nictypes = ctx['const'].all_values('NetworkAdapterType') + if args[1] not in list(nictypes.keys()): + print('%s not in acceptable values (%s)' % (args[1], list(nictypes.keys()))) + return (1, None) + adapter.adapterType = nictypes[args[1]] + return (0, None) + +def nicAttachmentSubCmd(ctx, vm, nicnum, adapter, args): + ''' + usage: nic <vm> <nicnum> attachment [Null|NAT|Bridged <interface>|Internal <name>|HostOnly <interface> + ''' + if len(args) == 1: + nicAttachmentType = { + ctx['global'].constants.NetworkAttachmentType_Null: ('Null', ''), + ctx['global'].constants.NetworkAttachmentType_NAT: ('NAT', ''), + ctx['global'].constants.NetworkAttachmentType_Bridged: ('Bridged', adapter.bridgedInterface), + ctx['global'].constants.NetworkAttachmentType_Internal: ('Internal', adapter.internalNetwork), + ctx['global'].constants.NetworkAttachmentType_HostOnly: ('HostOnly', adapter.hostOnlyInterface), + # @todo show details of the generic network attachment type + ctx['global'].constants.NetworkAttachmentType_Generic: ('Generic', ''), + } + if type(adapter.attachmentType) != int: + t = str(adapter.attachmentType) + else: + t = adapter.attachmentType + (r, p) = nicAttachmentType[t] + return (0, 'attachment:%s, name:%s' % (r, p)) + else: + nicAttachmentType = { + 'Null': { + 'v': lambda: len(args) == 2, + 'p': lambda: 'do nothing', + 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Null}, + 'NAT': { + 'v': lambda: len(args) == 2, + 'p': lambda: 'do nothing', + 'f': lambda: ctx['global'].constants.NetworkAttachmentType_NAT}, + 'Bridged': { + 'v': lambda: len(args) == 3, + 'p': lambda: adapter.__setattr__('bridgedInterface', args[2]), + 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Bridged}, + 'Internal': { + 'v': lambda: len(args) == 3, + 'p': lambda: adapter.__setattr__('internalNetwork', args[2]), + 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Internal}, + 'HostOnly': { + 'v': lambda: len(args) == 2, + 'p': lambda: adapter.__setattr__('hostOnlyInterface', args[2]), + 'f': lambda: ctx['global'].constants.NetworkAttachmentType_HostOnly}, + # @todo implement setting the properties of a generic attachment + 'Generic': { + 'v': lambda: len(args) == 3, + 'p': lambda: 'do nothing', + 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Generic} + } + if args[1] not in list(nicAttachmentType.keys()): + print('%s not in acceptable values (%s)' % (args[1], list(nicAttachmentType.keys()))) + return (1, None) + if not nicAttachmentType[args[1]]['v'](): + print(nicAttachmentType.__doc__) + return (1, None) + nicAttachmentType[args[1]]['p']() + adapter.attachmentType = nicAttachmentType[args[1]]['f']() + return (0, None) + +def nicCmd(ctx, args): + ''' + This command to manage network adapters + usage: nic <vm> <nicnum> <cmd> <cmd-args> + where cmd : attachment, trace, linespeed, cable, enable, type + ''' + # 'command name':{'runtime': is_callable_at_runtime, 'op': function_name} + niccomand = { + 'attachment': nicAttachmentSubCmd, + 'trace': nicTraceSubCmd, + 'linespeed': nicLineSpeedSubCmd, + 'cable': nicCableSubCmd, + 'enable': nicEnableSubCmd, + 'type': nicTypeSubCmd + } + if len(args) < 2 \ + or args[1] == 'help' \ + or (len(args) > 2 and args[3] not in niccomand): + if len(args) == 3 \ + and args[2] in niccomand: + print(niccomand[args[2]].__doc__) + else: + print(nicCmd.__doc__) + return 0 + + vm = ctx['argsToMach'](args) + if vm is None: + print('please specify vm') + return 0 + + if len(args) < 3 \ + or int(args[2]) not in list(range(0, ctx['vb'].systemProperties.getMaxNetworkAdapters(vm.chipsetType))): + print('please specify adapter num %d isn\'t in range [0-%d]'% (args[2], ctx['vb'].systemProperties.getMaxNetworkAdapters(vm.chipsetType))) + return 0 + nicnum = int(args[2]) + cmdargs = args[3:] + func = args[3] + session = None + session = ctx['global'].openMachineSession(vm, fPermitSharing=True) + vm = session.machine + adapter = vm.getNetworkAdapter(nicnum) + (rc, report) = niccomand[func](ctx, vm, nicnum, adapter, cmdargs) + if rc == 0: + vm.saveSettings() + if report is not None: + print('%s nic %d %s: %s' % (vm.name, nicnum, args[3], report)) + session.unlockMachine() + return 0 + + +def promptCmd(ctx, args): + if len(args) < 2: + print("Current prompt: '%s'" % (ctx['prompt'])) + return 0 + + ctx['prompt'] = args[1] + return 0 + +def foreachCmd(ctx, args): + if len(args) < 3: + print("usage: foreach scope command, where scope is XPath-like expression //vms/vm[@CPUCount='2']") + return 0 + + scope = args[1] + cmd = args[2] + elems = eval_xpath(ctx, scope) + try: + for e in elems: + e.apply(cmd) + except: + print("Error executing") + traceback.print_exc() + return 0 + +def foreachvmCmd(ctx, args): + if len(args) < 2: + print("foreachvm command <args>") + return 0 + cmdargs = args[1:] + cmdargs.insert(1, '') + for mach in getMachines(ctx): + cmdargs[1] = mach.id + runCommandArgs(ctx, cmdargs) + return 0 + +def recordDemoCmd(ctx, args): + if len(args) < 3: + print("usage: recordDemo vm filename (duration)") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + filename = args[2] + dur = 10000 + if len(args) > 3: + dur = float(args[3]) + cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: recordDemo(ctx, console, filename, dur)]) + return 0 + +def playbackDemoCmd(ctx, args): + if len(args) < 3: + print("usage: playbackDemo vm filename (duration)") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + filename = args[2] + dur = 10000 + if len(args) > 3: + dur = float(args[3]) + cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: playbackDemo(ctx, console, filename, dur)]) + return 0 + + +def pciAddr(ctx, addr): + strg = "%02x:%02x.%d" % (addr >> 8, (addr & 0xff) >> 3, addr & 7) + return colPci(ctx, strg) + +def lspci(ctx, console): + assigned = ctx['global'].getArray(console.machine, 'PCIDeviceAssignments') + for a in assigned: + if a.isPhysicalDevice: + print("%s: assigned host device %s guest %s" % (colDev(ctx, a.name), pciAddr(ctx, a.hostAddress), pciAddr(ctx, a.guestAddress))) + + atts = ctx['global'].getArray(console, 'attachedPCIDevices') + for a in atts: + if a.isPhysicalDevice: + print("%s: physical, guest %s, host %s" % (colDev(ctx, a.name), pciAddr(ctx, a.guestAddress), pciAddr(ctx, a.hostAddress))) + else: + print("%s: virtual, guest %s" % (colDev(ctx, a.name), pciAddr(ctx, a.guestAddress))) + return + +def parsePci(strg): + pcire = re.compile(r'(?P<b>[0-9a-fA-F]+):(?P<d>[0-9a-fA-F]+)\.(?P<f>\d)') + match = pcire.search(strg) + if match is None: + return -1 + pdict = match.groupdict() + return ((int(pdict['b'], 16)) << 8) | ((int(pdict['d'], 16)) << 3) | int(pdict['f']) + +def lspciCmd(ctx, args): + if len(args) < 2: + print("usage: lspci vm") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: lspci(ctx, console)]) + return 0 + +def attachpciCmd(ctx, args): + if len(args) < 3: + print("usage: attachpci vm hostpci <guestpci>") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + hostaddr = parsePci(args[2]) + if hostaddr == -1: + print("invalid host PCI %s, accepted format 01:02.3 for bus 1, device 2, function 3" % (args[2])) + return 0 + + if len(args) > 3: + guestaddr = parsePci(args[3]) + if guestaddr == -1: + print("invalid guest PCI %s, accepted format 01:02.3 for bus 1, device 2, function 3" % (args[3])) + return 0 + else: + guestaddr = hostaddr + cmdClosedVm(ctx, mach, lambda ctx, mach, a: mach.attachHostPCIDevice(hostaddr, guestaddr, True)) + return 0 + +def detachpciCmd(ctx, args): + if len(args) < 3: + print("usage: detachpci vm hostpci") + return 0 + mach = argsToMach(ctx, args) + if mach == None: + return 0 + hostaddr = parsePci(args[2]) + if hostaddr == -1: + print("invalid host PCI %s, accepted format 01:02.3 for bus 1, device 2, function 3" % (args[2])) + return 0 + + cmdClosedVm(ctx, mach, lambda ctx, mach, a: mach.detachHostPCIDevice(hostaddr)) + return 0 + +def gotoCmd(ctx, args): + if len(args) < 2: + print("usage: goto line") + return 0 + + line = int(args[1]) + + ctx['scriptLine'] = line + + return 0 + +aliases = {'s':'start', + 'i':'info', + 'l':'list', + 'h':'help', + 'a':'alias', + 'q':'quit', 'exit':'quit', + 'tg': 'typeGuest', + 'v':'verbose'} + +commands = {'help':['Prints help information', helpCmd, 0], + 'start':['Start virtual machine by name or uuid: start Linux headless', startCmd, 0], + 'createVm':['Create virtual machine: createVm macvm MacOS', createVmCmd, 0], + 'removeVm':['Remove virtual machine', removeVmCmd, 0], + 'pause':['Pause virtual machine', pauseCmd, 0], + 'resume':['Resume virtual machine', resumeCmd, 0], + 'save':['Save execution state of virtual machine', saveCmd, 0], + 'stats':['Stats for virtual machine', statsCmd, 0], + 'powerdown':['Power down virtual machine', powerdownCmd, 0], + 'powerbutton':['Effectively press power button', powerbuttonCmd, 0], + 'list':['Shows known virtual machines', listCmd, 0], + 'info':['Shows info on machine', infoCmd, 0], + 'ginfo':['Shows info on guest', ginfoCmd, 0], + 'gexec':['Executes program in the guest', gexecCmd, 0], + 'gcopy':['Copy file to the guest', gcopyCmd, 0], + 'gpipe':['Pipe between host and guest', gpipeCmd, 0], + 'alias':['Control aliases', aliasCmd, 0], + 'verbose':['Toggle verbosity', verboseCmd, 0], + 'setvar':['Set VMs variable: setvar Fedora BIOSSettings.ACPIEnabled True', setvarCmd, 0], + 'eval':['Evaluate arbitrary Python construction: eval \'for m in getMachines(ctx): print(m.name, "has", m.memorySize, "M")\'', evalCmd, 0], + 'quit':['Exits', quitCmd, 0], + 'host':['Show host information', hostCmd, 0], + 'guest':['Execute command for guest: guest Win32 \'console.mouse.putMouseEvent(20, 20, 0, 0, 0)\'', guestCmd, 0], + 'monitorGuest':['Monitor what happens with the guest for some time: monitorGuest Win32 10', monitorGuestCmd, 0], + 'monitorGuestKbd':['Monitor guest keyboard for some time: monitorGuestKbd Win32 10', monitorGuestKbdCmd, 0], + 'monitorGuestMouse':['Monitor guest mouse for some time: monitorGuestMouse Win32 10', monitorGuestMouseCmd, 0], + 'monitorGuestMultiTouch':['Monitor guest touch screen for some time: monitorGuestMultiTouch Win32 10', monitorGuestMultiTouchCmd, 0], + 'monitorVBox':['Monitor what happens with VirtualBox for some time: monitorVBox 10', monitorVBoxCmd, 0], + 'portForward':['Setup permanent port forwarding for a VM, takes adapter number host port and guest port: portForward Win32 0 8080 80', portForwardCmd, 0], + 'showLog':['Show log file of the VM, : showLog Win32', showLogCmd, 0], + 'findLog':['Show entries matching pattern in log file of the VM, : findLog Win32 PDM|CPUM', findLogCmd, 0], + 'findAssert':['Find assert in log file of the VM, : findAssert Win32', findAssertCmd, 0], + 'reloadExt':['Reload custom extensions: reloadExt', reloadExtCmd, 0], + 'runScript':['Run VBox script: runScript script.vbox', runScriptCmd, 0], + 'sleep':['Sleep for specified number of seconds: sleep 3.14159', sleepCmd, 0], + 'shell':['Execute external shell command: shell "ls /etc/rc*"', shellCmd, 0], + 'exportVm':['Export VM in OVF format: exportVm Win /tmp/win.ovf', exportVMCmd, 0], + 'screenshot':['Take VM screenshot to a file: screenshot Win /tmp/win.png 1024 768 0', screenshotCmd, 0], + 'teleport':['Teleport VM to another box (see openportal): teleport Win anotherhost:8000 <passwd> <maxDowntime>', teleportCmd, 0], + 'typeGuest':['Type arbitrary text in guest: typeGuest Linux "^lls\\n&UP;&BKSP;ess /etc/hosts\\nq^c" 0.7', typeGuestCmd, 0], + 'openportal':['Open portal for teleportation of VM from another box (see teleport): openportal Win 8000 <passwd>', openportalCmd, 0], + 'closeportal':['Close teleportation portal (see openportal, teleport): closeportal Win', closeportalCmd, 0], + 'getextra':['Get extra data, empty key lists all: getextra <vm|global> <key>', getExtraDataCmd, 0], + 'setextra':['Set extra data, empty value removes key: setextra <vm|global> <key> <value>', setExtraDataCmd, 0], + 'gueststats':['Print available guest stats (only Windows guests with additions so far): gueststats Win32', gueststatsCmd, 0], + 'plugcpu':['Add a CPU to a running VM: plugcpu Win 1', plugcpuCmd, 0], + 'unplugcpu':['Remove a CPU from a running VM (additions required, Windows cannot unplug): unplugcpu Linux 1', unplugcpuCmd, 0], + 'createHdd': ['Create virtual HDD: createHdd 1000 /disk.vdi ', createHddCmd, 0], + 'removeHdd': ['Permanently remove virtual HDD: removeHdd /disk.vdi', removeHddCmd, 0], + 'registerHdd': ['Register HDD image with VirtualBox instance: registerHdd /disk.vdi', registerHddCmd, 0], + 'unregisterHdd': ['Unregister HDD image with VirtualBox instance: unregisterHdd /disk.vdi', unregisterHddCmd, 0], + 'attachHdd': ['Attach HDD to the VM: attachHdd win /disk.vdi "IDE Controller" 0:1', attachHddCmd, 0], + 'detachHdd': ['Detach HDD from the VM: detachHdd win /disk.vdi', detachHddCmd, 0], + 'registerIso': ['Register CD/DVD image with VirtualBox instance: registerIso /os.iso', registerIsoCmd, 0], + 'unregisterIso': ['Unregister CD/DVD image with VirtualBox instance: unregisterIso /os.iso', unregisterIsoCmd, 0], + 'removeIso': ['Permanently remove CD/DVD image: removeIso /os.iso', removeIsoCmd, 0], + 'attachIso': ['Attach CD/DVD to the VM: attachIso win /os.iso "IDE Controller" 0:1', attachIsoCmd, 0], + 'detachIso': ['Detach CD/DVD from the VM: detachIso win /os.iso', detachIsoCmd, 0], + 'mountIso': ['Mount CD/DVD to the running VM: mountIso win /os.iso "IDE Controller" 0:1', mountIsoCmd, 0], + 'unmountIso': ['Unmount CD/DVD from running VM: unmountIso win "IDE Controller" 0:1', unmountIsoCmd, 0], + 'attachCtr': ['Attach storage controller to the VM: attachCtr win Ctr0 IDE ICH6', attachCtrCmd, 0], + 'detachCtr': ['Detach HDD from the VM: detachCtr win Ctr0', detachCtrCmd, 0], + 'attachUsb': ['Attach USB device to the VM (use listUsb to show available devices): attachUsb win uuid', attachUsbCmd, 0], + 'detachUsb': ['Detach USB device from the VM: detachUsb win uuid', detachUsbCmd, 0], + 'listMedia': ['List media known to this VBox instance', listMediaCmd, 0], + 'listUsb': ['List known USB devices', listUsbCmd, 0], + 'shareFolder': ['Make host\'s folder visible to guest: shareFolder win /share share writable', shareFolderCmd, 0], + 'unshareFolder': ['Remove folder sharing', unshareFolderCmd, 0], + 'gui': ['Start GUI frontend', guiCmd, 0], + 'colors':['Toggle colors', colorsCmd, 0], + 'snapshot':['VM snapshot manipulation, snapshot help for more info', snapshotCmd, 0], + 'nat':['NAT (network address translation engine) manipulation, nat help for more info', natCmd, 0], + 'nic' : ['Network adapter management', nicCmd, 0], + 'prompt' : ['Control shell prompt', promptCmd, 0], + 'foreachvm' : ['Perform command for each VM', foreachvmCmd, 0], + 'foreach' : ['Generic "for each" construction, using XPath-like notation: foreach //vms/vm[@OSTypeId=\'MacOS\'] "print(obj.name)"', foreachCmd, 0], + 'recordDemo':['Record demo: recordDemo Win32 file.dmo 10', recordDemoCmd, 0], + 'playbackDemo':['Playback demo: playbackDemo Win32 file.dmo 10', playbackDemoCmd, 0], + 'lspci': ['List PCI devices attached to the VM: lspci Win32', lspciCmd, 0], + 'attachpci': ['Attach host PCI device to the VM: attachpci Win32 01:00.0', attachpciCmd, 0], + 'detachpci': ['Detach host PCI device from the VM: detachpci Win32 01:00.0', detachpciCmd, 0], + 'goto': ['Go to line in script (script-only)', gotoCmd, 0] + } + +def runCommandArgs(ctx, args): + c = args[0] + if aliases.get(c, None) != None: + c = aliases[c] + ci = commands.get(c, None) + if ci == None: + print("Unknown command: '%s', type 'help' for list of known commands" % (c)) + return 0 + if ctx['remote'] and ctx['vb'] is None: + if c not in ['connect', 'reconnect', 'help', 'quit']: + print("First connect to remote server with %s command." % (colored('connect', 'blue'))) + return 0 + return ci[1](ctx, args) + + +def runCommand(ctx, cmd): + if not cmd: return 0 + args = split_no_quotes(cmd) + if len(args) == 0: return 0 + return runCommandArgs(ctx, args) + +# +# To write your own custom commands to vboxshell, create +# file ~/.VirtualBox/shellext.py with content like +# +# def runTestCmd(ctx, args): +# print("Testy test", ctx['vb']) +# return 0 +# +# commands = { +# 'test': ['Test help', runTestCmd] +# } +# and issue reloadExt shell command. +# This file also will be read automatically on startup or 'reloadExt'. +# +# Also one can put shell extensions into ~/.VirtualBox/shexts and +# they will also be picked up, so this way one can exchange +# shell extensions easily. +def addExtsFromFile(ctx, cmds, filename): + if not os.path.isfile(filename): + return + d = {} + try: + exec(compile(open(filename).read(), filename, 'exec'), d, d) + for (k, v) in list(d['commands'].items()): + if g_fVerbose: + print("customize: adding \"%s\" - %s" % (k, v[0])) + cmds[k] = [v[0], v[1], filename] + except: + print("Error loading user extensions from %s" % (filename)) + traceback.print_exc() + + +def checkUserExtensions(ctx, cmds, folder): + folder = str(folder) + name = os.path.join(folder, "shellext.py") + addExtsFromFile(ctx, cmds, name) + # also check 'exts' directory for all files + shextdir = os.path.join(folder, "shexts") + if not os.path.isdir(shextdir): + return + exts = os.listdir(shextdir) + for e in exts: + # not editor temporary files, please. + if e.endswith('.py'): + addExtsFromFile(ctx, cmds, os.path.join(shextdir, e)) + +def getHomeFolder(ctx): + if ctx['remote'] or ctx['vb'] is None: + if 'VBOX_USER_HOME' in os.environ: + return os.path.join(os.environ['VBOX_USER_HOME']) + return os.path.join(os.path.expanduser("~"), ".VirtualBox") + else: + return ctx['vb'].homeFolder + +def interpret(ctx): + if ctx['remote']: + commands['connect'] = ["Connect to remote VBox instance: connect http://server:18083 user password", connectCmd, 0] + commands['disconnect'] = ["Disconnect from remote VBox instance", disconnectCmd, 0] + commands['reconnect'] = ["Reconnect to remote VBox instance", reconnectCmd, 0] + ctx['wsinfo'] = ["http://localhost:18083", "", ""] + + vbox = ctx['vb'] + if vbox is not None: + try: + print("Running VirtualBox version %s" % (vbox.version)) + except Exception as e: + printErr(ctx, e) + if g_fVerbose: + traceback.print_exc() + ctx['perf'] = None # ctx['global'].getPerfCollector(vbox) + else: + ctx['perf'] = None + + home = getHomeFolder(ctx) + checkUserExtensions(ctx, commands, home) + if platform.system() in ['Windows', 'Microsoft']: + global g_fHasColors + g_fHasColors = False + hist_file = os.path.join(home, ".vboxshellhistory") + autoCompletion(commands, ctx) + + if g_fHasReadline and os.path.exists(hist_file): + readline.read_history_file(hist_file) + + # to allow to print actual host information, we collect info for + # last 150 secs maximum, (sample every 10 secs and keep up to 15 samples) + if ctx['perf']: + try: + ctx['perf'].setup(['*'], [vbox.host], 10, 15) + except: + pass + cmds = [] + + if g_sCmd is not None: + cmds = g_sCmd.split(';') + it = cmds.__iter__() + + while True: + try: + if g_fBatchMode: + cmd = 'runScript %s'% (g_sScriptFile) + elif g_sCmd is not None: + cmd = next(it) + else: + if sys.version_info[0] <= 2: + cmd = raw_input(ctx['prompt']) + else: + cmd = input(ctx['prompt']) + done = runCommand(ctx, cmd) + if done != 0: break + if g_fBatchMode: + break + except KeyboardInterrupt: + print('====== You can type quit or q to leave') + except StopIteration: + break + except EOFError: + break + except Exception as e: + printErr(ctx, e) + if g_fVerbose: + traceback.print_exc() + ctx['global'].waitForEvents(0) + try: + # There is no need to disable metric collection. This is just an example. + if ct['perf']: + ctx['perf'].disable(['*'], [vbox.host]) + except: + pass + if g_fHasReadline: + readline.write_history_file(hist_file) + +def runCommandCb(ctx, cmd, args): + args.insert(0, cmd) + return runCommandArgs(ctx, args) + +def runGuestCommandCb(ctx, uuid, guestLambda, args): + mach = machById(ctx, uuid) + if mach == None: + return 0 + args.insert(0, guestLambda) + cmdExistingVm(ctx, mach, 'guestlambda', args) + return 0 + +def main(argv): + + # + # Parse command line arguments. + # + parse = OptionParser() + parse.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False, help = "switch on verbose") + parse.add_option("-a", "--autopath", dest="autopath", action="store_true", default=False, help = "switch on autopath") + parse.add_option("-w", "--webservice", dest="style", action="store_const", const="WEBSERVICE", help = "connect to webservice") + parse.add_option("-b", "--batch", dest="batch_file", help = "script file to execute") + parse.add_option("-c", dest="command_line", help = "command sequence to execute") + parse.add_option("-o", dest="opt_line", help = "option line") + global g_fVerbose, g_sScriptFile, g_fBatchMode, g_fHasColors, g_fHasReadline, g_sCmd + (options, args) = parse.parse_args() + g_fVerbose = options.verbose + style = options.style + if options.batch_file is not None: + g_fBatchMode = True + g_fHasColors = False + g_fHasReadline = False + g_sScriptFile = options.batch_file + if options.command_line is not None: + g_fHasColors = False + g_fHasReadline = False + g_sCmd = options.command_line + + params = None + if options.opt_line is not None: + params = {} + strparams = options.opt_line + strparamlist = strparams.split(',') + for strparam in strparamlist: + (key, value) = strparam.split('=') + params[key] = value + + if options.autopath: + asLocations = [ os.getcwd(), ] + try: sScriptDir = os.path.dirname(os.path.abspath(__file__)) + except: pass # In case __file__ isn't there. + else: + if platform.system() in [ 'SunOS', ]: + asLocations.append(os.path.join(sScriptDir, 'amd64')) + asLocations.append(sScriptDir) + + + sPath = os.environ.get("VBOX_PROGRAM_PATH") + if sPath is None: + for sCurLoc in asLocations: + if os.path.isfile(os.path.join(sCurLoc, "VirtualBox")) \ + or os.path.isfile(os.path.join(sCurLoc, "VirtualBox.exe")): + print("Autodetected VBOX_PROGRAM_PATH as", sCurLoc) + os.environ["VBOX_PROGRAM_PATH"] = sCurLoc + sPath = sCurLoc + break + if sPath: + sys.path.append(os.path.join(sPath, "sdk", "installer")) + + sPath = os.environ.get("VBOX_SDK_PATH") + if sPath is None: + for sCurLoc in asLocations: + if os.path.isfile(os.path.join(sCurLoc, "sdk", "bindings", "VirtualBox.xidl")): + sCurLoc = os.path.join(sCurLoc, "sdk") + print("Autodetected VBOX_SDK_PATH as", sCurLoc) + os.environ["VBOX_SDK_PATH"] = sCurLoc + sPath = sCurLoc + break + if sPath: + sCurLoc = sPath + sTmp = os.path.join(sCurLoc, 'bindings', 'xpcom', 'python') + if os.path.isdir(sTmp): + sys.path.append(sTmp) + del sTmp + del sPath, asLocations + + + # + # Set up the shell interpreter context and start working. + # + from vboxapi import VirtualBoxManager + oVBoxMgr = VirtualBoxManager(style, params) + ctx = { + 'global': oVBoxMgr, + 'vb': oVBoxMgr.getVirtualBox(), + 'const': oVBoxMgr.constants, + 'remote': oVBoxMgr.remote, + 'type': oVBoxMgr.type, + 'run': lambda cmd, args: runCommandCb(ctx, cmd, args), + 'guestlambda': lambda uuid, guestLambda, args: runGuestCommandCb(ctx, uuid, guestLambda, args), + 'machById': lambda uuid: machById(ctx, uuid), + 'argsToMach': lambda args: argsToMach(ctx, args), + 'progressBar': lambda p: progressBar(ctx, p), + 'typeInGuest': typeInGuest, + '_machlist': None, + 'prompt': g_sPrompt, + 'scriptLine': 0, + 'interrupt': False, + } + interpret(ctx) + + # + # Release the interfaces references in ctx before cleaning up. + # + for sKey in list(ctx.keys()): + del ctx[sKey] + ctx = None + gc.collect() + + oVBoxMgr.deinit() + del oVBoxMgr + +if __name__ == '__main__': + main(sys.argv) + |