VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxShell/vboxshell.py@ 105971

Last change on this file since 105971 was 105971, checked in by vboxsync, 3 months ago

Main: Removed IHost::acceleration3DAvailable property, as this wasn't really doing anything useful and just will confuse API users. We now have ISystemProperties::supportedGraphicsFeatures. bugref:10749

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 125.3 KB
Line 
1#!/bin/sh
2# -*- coding: utf-8 -*-
3# pylint: disable=line-too-long
4# pylint: disable=too-many-statements
5# pylint: disable=deprecated-module
6# $Id: vboxshell.py 105971 2024-09-05 08:03:42Z vboxsync $
7
8# The following checks for the right (i.e. most recent) Python binary available
9# and re-starts the script using that binary (like a shell wrapper).
10#
11# Using a shebang like "#!/bin/env python" on newer Fedora/Debian distros is banned [1]
12# and also won't work on other newer distros (Ubuntu >= 23.10), as those only ship
13# python3 without a python->python3 symlink anymore.
14#
15# Note: As Python 2 is EOL, we consider this last (and hope for the best).
16#
17# [1] https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/message/2PD5RNJRKPN2DVTNGJSBHR5RUSVZSDZI/
18''':'
19for python_bin in python3 python python2
20do
21 type "$python_bin" > /dev/null 2>&1 && exec "$python_bin" "$0" "$@"
22done
23echo >&2 "ERROR: Python not found! Please install this first in order to run this program."
24exit 1
25':'''
26
27from __future__ import print_function
28
29# VirtualBox Python Shell.
30#
31# This program is a simple interactive shell for VirtualBox. You can query
32# information and issue commands from a simple command line.
33#
34# It also provides you with examples on how to use VirtualBox's Python API.
35# This shell is even somewhat documented, supports TAB-completion and
36# history if you have Python readline installed.
37#
38# Finally, shell allows arbitrary custom extensions, just create
39# .VirtualBox/shexts/ and drop your extensions there.
40# Enjoy.
41#
42# P.S. Our apologies for the code quality.
43
44__copyright__ = \
45"""
46Copyright (C) 2009-2023 Oracle and/or its affiliates.
47
48This file is part of VirtualBox base platform packages, as
49available from https://www.virtualbox.org.
50
51This program is free software; you can redistribute it and/or
52modify it under the terms of the GNU General Public License
53as published by the Free Software Foundation, in version 3 of the
54License.
55
56This program is distributed in the hope that it will be useful, but
57WITHOUT ANY WARRANTY; without even the implied warranty of
58MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
59General Public License for more details.
60
61You should have received a copy of the GNU General Public License
62along with this program; if not, see <https://www.gnu.org/licenses>.
63
64SPDX-License-Identifier: GPL-3.0-only
65"""
66__version__ = "$Revision: 105971 $"
67
68
69import gc
70import os
71import sys
72import traceback
73import shlex
74import tempfile
75import time
76import re
77import platform
78from optparse import OptionParser
79
80
81#
82# Global Variables
83#
84g_fBatchMode = False
85g_sScriptFile = None
86g_sCmd = None
87g_fHasReadline = True
88try:
89 import readline
90 import rlcompleter
91except ImportError:
92 g_fHasReadline = False
93
94g_sPrompt = "vbox> "
95
96g_fHasColors = True
97g_dTermColors = {
98 'red': '\033[31m',
99 'blue': '\033[94m',
100 'green': '\033[92m',
101 'yellow': '\033[93m',
102 'magenta': '\033[35m',
103 'cyan': '\033[36m'
104}
105
106
107
108def colored(strg, color):
109 """
110 Translates a string to one including coloring settings, if enabled.
111 """
112 if not g_fHasColors:
113 return strg
114 col = g_dTermColors.get(color, None)
115 if col:
116 return col+str(strg)+'\033[0m'
117 return strg
118
119if g_fHasReadline:
120 class CompleterNG(rlcompleter.Completer):
121 def __init__(self, dic, ctx):
122 self.ctx = ctx
123 rlcompleter.Completer.__init__(self, dic)
124
125 def complete(self, text, state):
126 """
127 taken from:
128 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496812
129 """
130 if text == "":
131 return ['\t', None][state]
132 return rlcompleter.Completer.complete(self, text, state)
133
134 def canBePath(self, _phrase, word):
135 return word.startswith('/')
136
137 def canBeCommand(self, phrase, _word):
138 spaceIdx = phrase.find(" ")
139 begIdx = readline.get_begidx()
140 firstWord = (spaceIdx == -1 or begIdx < spaceIdx)
141 if firstWord:
142 return True
143 if phrase.startswith('help'):
144 return True
145 return False
146
147 def canBeMachine(self, phrase, word):
148 return not self.canBePath(phrase, word) and not self.canBeCommand(phrase, word)
149
150 def global_matches(self, text):
151 """
152 Compute matches when text is a simple name.
153 Return a list of all names currently defined
154 in self.namespace that match.
155 """
156
157 matches = []
158 phrase = readline.get_line_buffer()
159
160 try:
161 if self.canBePath(phrase, text):
162 (directory, rest) = os.path.split(text)
163 c = len(rest)
164 for word in os.listdir(directory):
165 if c == 0 or word[:c] == rest:
166 matches.append(os.path.join(directory, word))
167
168 if self.canBeCommand(phrase, text):
169 c = len(text)
170 for lst in [ self.namespace ]:
171 for word in lst:
172 if word[:c] == text:
173 matches.append(word)
174
175 if self.canBeMachine(phrase, text):
176 c = len(text)
177 for mach in getMachines(self.ctx, False, True):
178 # although it has autoconversion, we need to cast
179 # explicitly for subscripts to work
180 word = re.sub("(?<!\\\\) ", "\\ ", str(mach.name))
181 if word[:c] == text:
182 matches.append(word)
183 word = str(mach.id)
184 if word[:c] == text:
185 matches.append(word)
186
187 except Exception as e:
188 printErr(self.ctx, e)
189 if g_fVerbose:
190 traceback.print_exc()
191
192 return matches
193
194def autoCompletion(cmds, ctx):
195 if not g_fHasReadline:
196 return
197
198 comps = {}
199 for (key, _value) in list(cmds.items()):
200 comps[key] = None
201 completer = CompleterNG(comps, ctx)
202 readline.set_completer(completer.complete)
203 delims = readline.get_completer_delims()
204 readline.set_completer_delims(re.sub("[\\./-]", "", delims)) # remove some of the delimiters
205 readline.parse_and_bind("set editing-mode emacs")
206 # OSX need it
207 if platform.system() == 'Darwin':
208 # see http://www.certif.com/spec_help/readline.html
209 readline.parse_and_bind ("bind ^I rl_complete")
210 readline.parse_and_bind ("bind ^W ed-delete-prev-word")
211 # Doesn't work well
212 # readline.parse_and_bind ("bind ^R em-inc-search-prev")
213 readline.parse_and_bind("tab: complete")
214
215
216g_fVerbose = False
217
218def split_no_quotes(s):
219 return shlex.split(s)
220
221def progressBar(ctx, progress, wait=1000):
222 try:
223 while not progress.completed:
224 print("%s %%\r" % (colored(str(progress.percent), 'red')), end="")
225 sys.stdout.flush()
226 progress.waitForCompletion(wait)
227 ctx['global'].waitForEvents(0)
228 if int(progress.resultCode) != 0:
229 reportError(ctx, progress)
230 return 1
231 except KeyboardInterrupt:
232 print("Interrupted.")
233 ctx['interrupt'] = True
234 if progress.cancelable:
235 print("Canceling task...")
236 progress.cancel()
237 return 0
238
239def printErr(_ctx, e):
240 oVBoxMgr = _ctx['global']
241 if oVBoxMgr.xcptIsOurXcptKind(e):
242 print(colored('%s: %s' % (oVBoxMgr.xcptToString(e), oVBoxMgr.xcptGetMessage(e)), 'red'))
243 else:
244 print(colored(str(e), 'red'))
245
246def reportError(_ctx, progress):
247 errorinfo = progress.errorInfo
248 if errorinfo:
249 print(colored("Error in module '%s': %s" % (errorinfo.component, errorinfo.text), 'red'))
250
251def colCat(_ctx, strg):
252 return colored(strg, 'magenta')
253
254def colVm(_ctx, vmname):
255 return colored(vmname, 'blue')
256
257def colPath(_ctx, path):
258 return colored(path, 'green')
259
260def colSize(_ctx, byte):
261 return colored(byte, 'red')
262
263def colPci(_ctx, pcidev):
264 return colored(pcidev, 'green')
265
266def colDev(_ctx, pcidev):
267 return colored(pcidev, 'cyan')
268
269def colSizeM(_ctx, mbyte):
270 return colored(str(mbyte)+'M', 'red')
271
272def platformArchFromString(ctx, arch):
273 if arch in [ 'x86', 'x86_64', 'x64' ]:
274 return ctx['global'].constants.PlatformArchitecture_x86
275 if arch in ['arm', 'aarch32', 'aarch64' ]:
276 return ctx['global'].constants.PlatformArchitecture_ARM
277 return ctx['global'].constants.PlatformArchitecture_None
278
279def createVm(ctx, name, arch, kind):
280 vbox = ctx['vb']
281 enmArch = platformArchFromString(ctx, arch)
282 if enmArch == ctx['global'].constants.PlatformArchitecture_None:
283 print("wrong / invalid platform architecture specified!")
284 return
285 sFlags = ''
286 sCipher = '' ## @todo No encryption support here yet!
287 sPasswordID = ''
288 sPassword = ''
289 mach = vbox.createMachine("", name, enmArch, [], kind, sFlags, sCipher, sPasswordID, sPassword)
290 mach.saveSettings()
291 print("created machine with UUID", mach.id)
292 vbox.registerMachine(mach)
293 # update cache
294 getMachines(ctx, True)
295
296def removeVm(ctx, mach):
297 uuid = mach.id
298 print("removing machine ", mach.name, "with UUID", uuid)
299 cmdClosedVm(ctx, mach, detachVmDevice, ["ALL"])
300 disks = mach.unregister(ctx['global'].constants.CleanupMode_Full)
301 if mach:
302 progress = mach.deleteConfig(disks)
303 if progressBar(ctx, progress, 100) and int(progress.resultCode) == 0:
304 print("Success!")
305 else:
306 reportError(ctx, progress)
307 # update cache
308 getMachines(ctx, True)
309
310def startVm(ctx, mach, vmtype):
311 perf = ctx['perf']
312 session = ctx['global'].getSessionObject()
313 asEnv = []
314 progress = mach.launchVMProcess(session, vmtype, asEnv)
315 if progressBar(ctx, progress, 100) and int(progress.resultCode) == 0:
316 # we ignore exceptions to allow starting VM even if
317 # perf collector cannot be started
318 if perf:
319 try:
320 perf.setup(['*'], [mach], 10, 15)
321 except Exception as e:
322 printErr(ctx, e)
323 if g_fVerbose:
324 traceback.print_exc()
325 session.unlockMachine()
326
327class CachedMach:
328 def __init__(self, mach):
329 if mach.accessible:
330 self.name = mach.name
331 else:
332 self.name = '<inaccessible>'
333 self.id = mach.id # pylint: disable=invalid-name
334
335def cacheMachines(_ctx, lst):
336 result = []
337 for mach in lst:
338 elem = CachedMach(mach)
339 result.append(elem)
340 return result
341
342def getMachines(ctx, invalidate = False, simple=False):
343 if ctx['vb'] is not None:
344 if ctx['_machlist'] is None or invalidate:
345 ctx['_machlist'] = ctx['global'].getArray(ctx['vb'], 'machines')
346 ctx['_machlistsimple'] = cacheMachines(ctx, ctx['_machlist'])
347 if simple:
348 return ctx['_machlistsimple']
349 return ctx['_machlist']
350 return []
351
352def asState(var):
353 if var:
354 return colored('on', 'green')
355 return colored('off', 'green')
356
357def asFlag(var):
358 if var:
359 return 'yes'
360 return 'no'
361
362def getFacilityStatus(ctx, guest, facilityType):
363 (status, _timestamp) = guest.getFacilityStatus(facilityType)
364 return asEnumElem(ctx, 'AdditionsFacilityStatus', status)
365
366def perfStats(ctx, mach):
367 if not ctx['perf']:
368 return
369 for metric in ctx['perf'].query(["*"], [mach]):
370 print(metric['name'], metric['values_as_string'])
371
372def guestExec(_ctx, _machine, _console, cmds):
373 exec(cmds) # pylint: disable=exec-used
374
375def printMouseEvent(_ctx, mev):
376 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))
377
378def printKbdEvent(ctx, kev):
379 print("Kbd: ", ctx['global'].getArray(kev, 'scancodes'))
380
381def printMultiTouchEvent(ctx, mtev):
382 print("MultiTouch: %s contacts=%d time=%d" \
383 % ("touchscreen" if mtev.isTouchScreen else "touchpad", mtev.contactCount, mtev.scanTime))
384 xPositions = ctx['global'].getArray(mtev, 'xPositions')
385 yPositions = ctx['global'].getArray(mtev, 'yPositions')
386 contactIds = ctx['global'].getArray(mtev, 'contactIds')
387 contactFlags = ctx['global'].getArray(mtev, 'contactFlags')
388
389 for i in range(0, mtev.contactCount):
390 print(" [%d] %d,%d %d %d" % (i, xPositions[i], yPositions[i], contactIds[i], contactFlags[i]))
391
392def monitorSource(ctx, eventSource, active, dur):
393 def handleEventImpl(event):
394 evtype = event.type
395 print("got event: %s %s" % (str(evtype), asEnumElem(ctx, 'VBoxEventType', evtype)))
396 if evtype == ctx['global'].constants.VBoxEventType_OnMachineStateChanged:
397 scev = ctx['global'].queryInterface(event, 'IMachineStateChangedEvent')
398 if scev:
399 print("machine state event: mach=%s state=%s" % (scev.machineId, scev.state))
400 elif evtype == ctx['global'].constants.VBoxEventType_OnSnapshotTaken:
401 stev = ctx['global'].queryInterface(event, 'ISnapshotTakenEvent')
402 if stev:
403 print("snapshot taken event: mach=%s snap=%s" % (stev.machineId, stev.snapshotId))
404 elif evtype == ctx['global'].constants.VBoxEventType_OnGuestPropertyChanged:
405 gpcev = ctx['global'].queryInterface(event, 'IGuestPropertyChangedEvent')
406 if gpcev:
407 if gpcev.fWasDeleted is True:
408 print("property %s was deleted" % (gpcev.name))
409 else:
410 print("guest property change: name=%s value=%s flags='%s'" %
411 (gpcev.name, gpcev.value, gpcev.flags))
412 elif evtype == ctx['global'].constants.VBoxEventType_OnMousePointerShapeChanged:
413 psev = ctx['global'].queryInterface(event, 'IMousePointerShapeChangedEvent')
414 if psev:
415 shape = ctx['global'].getArray(psev, 'shape')
416 if shape is None:
417 print("pointer shape event - empty shape")
418 else:
419 print("pointer shape event: w=%d h=%d shape len=%d" % (psev.width, psev.height, len(shape)))
420 elif evtype == ctx['global'].constants.VBoxEventType_OnGuestMouse:
421 mev = ctx['global'].queryInterface(event, 'IGuestMouseEvent')
422 if mev:
423 printMouseEvent(ctx, mev)
424 elif evtype == ctx['global'].constants.VBoxEventType_OnGuestKeyboard:
425 kev = ctx['global'].queryInterface(event, 'IGuestKeyboardEvent')
426 if kev:
427 printKbdEvent(ctx, kev)
428 elif evtype == ctx['global'].constants.VBoxEventType_OnGuestMultiTouch:
429 mtev = ctx['global'].queryInterface(event, 'IGuestMultiTouchEvent')
430 if mtev:
431 printMultiTouchEvent(ctx, mtev)
432
433 class EventListener(object):
434 def __init__(self, arg):
435 pass
436
437 def handleEvent(self, event):
438 try:
439 # a bit convoluted QI to make it work with MS COM
440 handleEventImpl(ctx['global'].queryInterface(event, 'IEvent'))
441 except:
442 traceback.print_exc()
443
444 if active:
445 listener = ctx['global'].createListener(EventListener)
446 else:
447 listener = eventSource.createListener()
448 registered = False
449 if dur == -1:
450 # not infinity, but close enough
451 dur = 100000
452 try:
453 eventSource.registerListener(listener, [ctx['global'].constants.VBoxEventType_Any], active)
454 registered = True
455 end = time.time() + dur
456 while time.time() < end:
457 if active:
458 ctx['global'].waitForEvents(500)
459 else:
460 event = eventSource.getEvent(listener, 500)
461 if event:
462 handleEventImpl(event)
463 # otherwise waitable events will leak (active listeners ACK automatically)
464 eventSource.eventProcessed(listener, event)
465 # We need to catch all exceptions here, otherwise listener will never be unregistered
466 except:
467 traceback.print_exc()
468
469 if listener and registered:
470 eventSource.unregisterListener(listener)
471
472
473g_tsLast = 0
474def recordDemo(ctx, console, filename, dur):
475 global g_tsLast
476 g_tsLast = time.time()
477
478 def stamp():
479 global g_tsLast
480 tsCur = time.time()
481 timePassed = int((tsCur-g_tsLast)*1000)
482 g_tsLast = tsCur
483 return timePassed
484
485 def handleEventImpl(event):
486 evtype = event.type
487 #print("got event: %s %s" % (str(evtype), asEnumElem(ctx, 'VBoxEventType', evtype)))
488 if evtype == ctx['global'].constants.VBoxEventType_OnGuestMouse:
489 mev = ctx['global'].queryInterface(event, 'IGuestMouseEvent')
490 if mev:
491 line = "%d: m %d %d %d %d %d %d\n" % (stamp(), mev.mode, mev.x, mev.y, mev.z, mev.w, mev.buttons)
492 demo.write(line)
493 elif evtype == ctx['global'].constants.VBoxEventType_OnGuestKeyboard:
494 kev = ctx['global'].queryInterface(event, 'IGuestKeyboardEvent')
495 if kev:
496 line = "%d: k %s\n" % (stamp(), str(ctx['global'].getArray(kev, 'scancodes')))
497 demo.write(line)
498
499 listener = console.eventSource.createListener()
500 registered = False
501 # we create an aggregated event source to listen for multiple event sources (keyboard and mouse in our case)
502 agg = console.eventSource.createAggregator([console.keyboard.eventSource, console.mouse.eventSource])
503 with open(filename, 'w', encoding='utf-8') as demo:
504 header = "VM=" + console.machine.name + "\n"
505 demo.write(header)
506 if dur == -1:
507 # not infinity, but close enough
508 dur = 100000
509 try:
510 agg.registerListener(listener, [ctx['global'].constants.VBoxEventType_Any], False)
511 registered = True
512 end = time.time() + dur
513 while time.time() < end:
514 event = agg.getEvent(listener, 1000)
515 if event:
516 handleEventImpl(event)
517 # keyboard/mouse events aren't waitable, so no need for eventProcessed
518 # We need to catch all exceptions here, otherwise listener will never be unregistered
519 except:
520 traceback.print_exc()
521
522 demo.close()
523 if listener and registered:
524 agg.unregisterListener(listener)
525
526
527def playbackDemo(ctx, console, filename, dur):
528 if dur == -1:
529 # not infinity, but close enough
530 dur = 100000
531 with open(filename, 'r', encoding='utf-8') as demo:
532 header = demo.readline()
533 if g_fVerbose:
534 print("Header is", header)
535 basere = re.compile(r'(?P<s>\d+): (?P<t>[km]) (?P<p>.*)')
536 mre = re.compile(r'(?P<a>\d+) (?P<x>-*\d+) (?P<y>-*\d+) (?P<z>-*\d+) (?P<w>-*\d+) (?P<b>-*\d+)')
537 kre = re.compile(r'\d+')
538
539 kbd = console.keyboard
540 mouse = console.mouse
541
542 try:
543 end = time.time() + dur
544 for line in demo:
545 if time.time() > end:
546 break
547 match = basere.search(line)
548 if match is None:
549 continue
550
551 rdict = match.groupdict()
552 stamp = rdict['s']
553 params = rdict['p']
554 rtype = rdict['t']
555
556 time.sleep(float(stamp)/1000)
557
558 if rtype == 'k':
559 codes = kre.findall(params)
560 if g_fVerbose:
561 print("KBD:", codes)
562 kbd.putScancodes(codes)
563 elif rtype == 'm':
564 mouseEvent = mre.search(params)
565 if mouseEvent is not None:
566 mdict = mouseEvent.groupdict()
567 if mdict['a'] == '1':
568 if g_fVerbose:
569 print("MA: ", mdict['x'], mdict['y'], mdict['z'], mdict['b'])
570 mouse.putMouseEventAbsolute(int(mdict['x']), int(mdict['y']), int(mdict['z']), int(mdict['w']), int(mdict['b']))
571 else:
572 if g_fVerbose:
573 print("MR: ", mdict['x'], mdict['y'], mdict['b'])
574 mouse.putMouseEvent(int(mdict['x']), int(mdict['y']), int(mdict['z']), int(mdict['w']), int(mdict['b']))
575
576 # We need to catch all exceptions here, to close file
577 except KeyboardInterrupt:
578 ctx['interrupt'] = True
579 except:
580 traceback.print_exc()
581
582 demo.close()
583
584def takeScreenshot(ctx, console, args):
585 display = console.display
586 if len(args) > 0:
587 filename = args[0]
588 else:
589 filename = os.path.join(tempfile.gettempdir(), "screenshot.png")
590 if len(args) > 3:
591 screen = int(args[3])
592 else:
593 screen = 0
594 (fbw, fbh, _fbbpp, _fbx, _fby, _) = display.getScreenResolution(screen)
595 if len(args) > 1:
596 width = int(args[1])
597 else:
598 width = fbw
599 if len(args) > 2:
600 height = int(args[2])
601 else:
602 height = fbh
603
604 print("Saving screenshot (%d x %d) screen %d in %s..." % (width, height, screen, filename))
605 data = display.takeScreenShotToArray(screen, width, height, ctx['const'].BitmapFormat_PNG)
606 with open(filename, 'wb') as pngfile:
607 pngfile.write(data)
608 pngfile.close()
609
610def teleport(ctx, _session, console, args):
611 if args[0].find(":") == -1:
612 print("Use host:port format for teleport target")
613 return
614 (host, port) = args[0].split(":")
615 if len(args) > 1:
616 passwd = args[1]
617 else:
618 passwd = ""
619
620 if len(args) > 2:
621 maxDowntime = int(args[2])
622 else:
623 maxDowntime = 250
624
625 port = int(port)
626 print("Teleporting to %s:%d..." % (host, port))
627 progress = console.teleport(host, port, passwd, maxDowntime)
628 if progressBar(ctx, progress, 100) and int(progress.resultCode) == 0:
629 print("Success!")
630 else:
631 reportError(ctx, progress)
632
633
634def guestStats(ctx, console, args):
635 guest = console.guest
636 if not guest:
637 print("Guest is not in a running state")
638 return
639 # we need to set up guest statistics
640 if len(args) > 0 :
641 update = args[0]
642 else:
643 update = 1
644 if guest.statisticsUpdateInterval != update:
645 guest.statisticsUpdateInterval = update
646 try:
647 time.sleep(float(update)+0.1)
648 except:
649 # to allow sleep interruption
650 pass
651 all_stats = ctx['const'].all_values('GuestStatisticType')
652 cpu = 0
653 for s in list(all_stats.keys()):
654 try:
655 val = guest.getStatistic( cpu, all_stats[s])
656 print("%s: %d" % (s, val))
657 except:
658 # likely not implemented
659 pass
660
661def plugCpu(_ctx, machine, _session, args):
662 cpu = int(args[0])
663 print("Adding CPU %d..." % (cpu))
664 machine.hotPlugCPU(cpu)
665
666def unplugCpu(_ctx, machine, _session, args):
667 cpu = int(args[0])
668 print("Removing CPU %d..." % (cpu))
669 machine.hotUnplugCPU(cpu)
670
671def mountIso(_ctx, machine, _session, args):
672 machine.mountMedium(args[0], args[1], args[2], args[3], args[4])
673 machine.saveSettings()
674
675def cond(condToCheck, resTrue, resFalse):
676 if condToCheck:
677 return resTrue
678 return resFalse
679
680def printHostUsbDev(ctx, usbdev):
681 print(" %s: %s (vendorId=%d productId=%d serial=%s) %s" \
682 % (usbdev.id, colored(usbdev.product, 'blue'), usbdev.vendorId, usbdev.productId, usbdev.serialNumber, asEnumElem(ctx, 'USBDeviceState', usbdev.state)))
683
684def printUsbDev(_ctx, usbdev):
685 print(" %s: %s (vendorId=%d productId=%d serial=%s)" \
686 % (usbdev.id, colored(usbdev.product, 'blue'), usbdev.vendorId, usbdev.productId, usbdev.serialNumber))
687
688def printSf(ctx, sharedfolder):
689 print(" name=%s host=%s %s %s" \
690 % (sharedfolder.name, colPath(ctx, sharedfolder.hostPath), cond(sharedfolder.accessible, "accessible", "not accessible"), cond(sharedfolder.writable, "writable", "read-only")))
691
692def ginfo(ctx, console, _args):
693 guest = console.guest
694 if not guest:
695 print("Guest is not in a running state")
696 return
697 if guest.additionsRunLevel != ctx['const'].AdditionsRunLevelType_None:
698 print("Additions active, version %s" % (guest.additionsVersion))
699 print("Support seamless: %s" % (getFacilityStatus(ctx, guest, ctx['const'].AdditionsFacilityType_Seamless)))
700 print("Support graphics: %s" % (getFacilityStatus(ctx, guest, ctx['const'].AdditionsFacilityType_Graphics)))
701 print("Balloon size: %d" % (guest.memoryBalloonSize))
702 print("Statistic update interval: %d" % (guest.statisticsUpdateInterval))
703 else:
704 print("No additions")
705 usbs = ctx['global'].getArray(console, 'USBDevices')
706 print("Attached USB:")
707 for usbdev in usbs:
708 printUsbDev(ctx, usbdev)
709 rusbs = ctx['global'].getArray(console, 'remoteUSBDevices')
710 print("Remote USB:")
711 for usbdev in rusbs:
712 printHostUsbDev(ctx, usbdev)
713 print("Transient shared folders:")
714 sfs = rusbs = ctx['global'].getArray(console, 'sharedFolders')
715 for sharedfolder in sfs:
716 printSf(ctx, sharedfolder)
717
718def cmdExistingVm(ctx, mach, cmd, args):
719 session = None
720 try:
721 session = ctx['global'].openMachineSession(mach, fPermitSharing=True)
722 except Exception as e:
723 printErr(ctx, "Session to '%s' not open: %s" % (mach.name, str(e)))
724 if g_fVerbose:
725 traceback.print_exc()
726 return
727 if session.state != ctx['const'].SessionState_Locked:
728 print("Session to '%s' in wrong state: %s" % (mach.name, session.state))
729 session.unlockMachine()
730 return
731 # this could be an example how to handle local only (i.e. unavailable
732 # in Webservices) functionality
733 if ctx['remote'] and cmd == 'some_local_only_command':
734 print('Trying to use local only functionality, ignored')
735 session.unlockMachine()
736 return
737 console = session.console
738 ops = {'pause': console.pause(),
739 'resume': console.resume(),
740 'powerdown': console.powerDown(),
741 'powerbutton': console.powerButton(),
742 'stats': lambda: perfStats(ctx, mach),
743 'guest': lambda: guestExec(ctx, mach, console, args),
744 'ginfo': lambda: ginfo(ctx, console, args),
745 'guestlambda': lambda: args[0](ctx, mach, console, args[1:]),
746 'save': lambda: progressBar(ctx, session.machine.saveState()),
747 'screenshot': lambda: takeScreenshot(ctx, console, args),
748 'teleport': lambda: teleport(ctx, session, console, args),
749 'gueststats': lambda: guestStats(ctx, console, args),
750 'plugcpu': lambda: plugCpu(ctx, session.machine, session, args),
751 'unplugcpu': lambda: unplugCpu(ctx, session.machine, session, args),
752 'mountiso': lambda: mountIso(ctx, session.machine, session, args),
753 }
754 try:
755 ops[cmd]()
756 except KeyboardInterrupt:
757 ctx['interrupt'] = True
758 except Exception as e:
759 printErr(ctx, e)
760 if g_fVerbose:
761 traceback.print_exc()
762
763 session.unlockMachine()
764
765
766def cmdClosedVm(ctx, mach, cmd, args=None, save=True):
767 session = ctx['global'].openMachineSession(mach, fPermitSharing=True)
768 mach = session.machine
769 try:
770 cmd(ctx, mach, args)
771 except Exception as e:
772 save = False
773 printErr(ctx, e)
774 if g_fVerbose:
775 traceback.print_exc()
776 if save:
777 try:
778 mach.saveSettings()
779 except Exception as e:
780 printErr(ctx, e)
781 if g_fVerbose:
782 traceback.print_exc()
783 ctx['global'].closeMachineSession(session)
784
785
786def cmdAnyVm(ctx, mach, cmd, args=None, save=False):
787 session = ctx['global'].openMachineSession(mach, fPermitSharing=True)
788 mach = session.machine
789 try:
790 cmd(ctx, mach, session.console, args)
791 except Exception as e:
792 save = False
793 printErr(ctx, e)
794 if g_fVerbose:
795 traceback.print_exc()
796 if save:
797 mach.saveSettings()
798 ctx['global'].closeMachineSession(session)
799
800def machById(ctx, uuid):
801 mach = ctx['vb'].findMachine(uuid)
802 return mach
803
804class XPathNode:
805 def __init__(self, parent, obj, ntype):
806 self.parent = parent
807 self.obj = obj
808 self.ntype = ntype
809 def lookup(self, subpath):
810 children = self.enum()
811 matches = []
812 for e in children:
813 if e.matches(subpath):
814 matches.append(e)
815 return matches
816 def enum(self):
817 return []
818 def matches(self, subexp):
819 if subexp == self.ntype:
820 return True
821 if not subexp.startswith(self.ntype):
822 return False
823 match = re.search(r"@(?P<a>\w+)=(?P<v>[^\'\[\]]+)", subexp)
824 matches = False
825 try:
826 if match is not None:
827 xdict = match.groupdict()
828 attr = xdict['a']
829 val = xdict['v']
830 matches = str(getattr(self.obj, attr)) == val
831 except:
832 pass
833 return matches
834 def apply(self, cmd):
835 exec(cmd, {'obj':self.obj, 'node':self, 'ctx':self.getCtx()}, {}) # pylint: disable=exec-used
836 def getCtx(self):
837 if hasattr(self, 'ctx'):
838 return self.ctx
839 return self.parent.getCtx()
840
841class XPathNodeHolder(XPathNode):
842 def __init__(self, parent, obj, attr, heldClass, xpathname):
843 XPathNode.__init__(self, parent, obj, 'hld '+xpathname)
844 self.attr = attr
845 self.heldClass = heldClass
846 self.xpathname = xpathname
847 def enum(self):
848 children = []
849 for node in self.getCtx()['global'].getArray(self.obj, self.attr):
850 nodexml = self.heldClass(self, node)
851 children.append(nodexml)
852 return children
853 def matches(self, subexp):
854 return subexp == self.xpathname
855
856class XPathNodeValue(XPathNode):
857 def __init__(self, parent, obj, xpathname):
858 XPathNode.__init__(self, parent, obj, 'val '+xpathname)
859 self.xpathname = xpathname
860 def matches(self, subexp):
861 return subexp == self.xpathname
862
863class XPathNodeHolderVM(XPathNodeHolder):
864 def __init__(self, parent, vbox):
865 XPathNodeHolder.__init__(self, parent, vbox, 'machines', XPathNodeVM, 'vms')
866
867class XPathNodeVM(XPathNode):
868 def __init__(self, parent, obj):
869 XPathNode.__init__(self, parent, obj, 'vm')
870 #def matches(self, subexp):
871 # return subexp=='vm'
872 def enum(self):
873 return [XPathNodeHolderNIC(self, self.obj),
874 XPathNodeValue(self, self.obj.BIOSSettings, 'bios'), ]
875
876class XPathNodeHolderNIC(XPathNodeHolder):
877 def __init__(self, parent, mach):
878 XPathNodeHolder.__init__(self, parent, mach, 'nics', XPathNodeVM, 'nics')
879 self.maxNic = mach.platform.properties.getMaxNetworkAdapters(mach.platform.chipsetType)
880 def enum(self):
881 children = []
882 for i in range(0, self.maxNic):
883 node = XPathNodeNIC(self, self.obj.getNetworkAdapter(i))
884 children.append(node)
885 return children
886
887class XPathNodeNIC(XPathNode):
888 def __init__(self, parent, obj):
889 XPathNode.__init__(self, parent, obj, 'nic')
890 def matches(self, subexp):
891 return subexp == 'nic'
892
893class XPathNodeRoot(XPathNode):
894 def __init__(self, ctx):
895 XPathNode.__init__(self, None, None, 'root')
896 self.ctx = ctx
897 def enum(self):
898 return [XPathNodeHolderVM(self, self.ctx['vb'])]
899 def matches(self, subexp):
900 return True
901
902def eval_xpath(ctx, scope):
903 pathnames = scope.split("/")[2:]
904 nodes = [XPathNodeRoot(ctx)]
905 for path in pathnames:
906 seen = []
907 while len(nodes) > 0:
908 node = nodes.pop()
909 seen.append(node)
910 for s in seen:
911 matches = s.lookup(path)
912 for match in matches:
913 nodes.append(match)
914 if len(nodes) == 0:
915 break
916 return nodes
917
918def argsToMach(ctx, args):
919 if len(args) < 2:
920 print("usage: %s <vmname|uuid>" % (args[0]))
921 return None
922 uuid = args[1]
923 mach = machById(ctx, uuid)
924 if not mach:
925 print("Machine '%s' is unknown, use list command to find available machines" % (uuid))
926 return mach
927
928def helpSingleCmd(cmd, help_text, from_ext):
929 if from_ext != 0:
930 spec = " [ext from "+from_ext+"]"
931 else:
932 spec = ""
933 print(" %s: %s%s" % (colored(cmd, 'blue'), help_text, spec))
934
935def helpCmd(_ctx, args):
936 if len(args) == 1:
937 print("Help page:")
938 names = list(commands.keys())
939 names.sort()
940 for i in names:
941 helpSingleCmd(i, commands[i][0], commands[i][2])
942 else:
943 cmd = args[1]
944 c = commands.get(cmd)
945 if not c:
946 print("Command '%s' not known" % (cmd))
947 else:
948 helpSingleCmd(cmd, c[0], c[2])
949 return 0
950
951def asEnumElem(ctx, enum, elem):
952 enumVals = ctx['const'].all_values(enum)
953 for e in list(enumVals.keys()):
954 if str(elem) == str(enumVals[e]):
955 return colored(e, 'green')
956 return colored("<unknown>", 'green')
957
958def enumFromString(ctx, enum, strg):
959 enumVals = ctx['const'].all_values(enum)
960 return enumVals.get(strg, None)
961
962def listCmd(ctx, _args):
963 for mach in getMachines(ctx, True):
964 try:
965 if mach.teleporterEnabled:
966 tele = "[T] "
967 else:
968 tele = " "
969 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)))
970 except Exception as e:
971 printErr(ctx, e)
972 if g_fVerbose:
973 traceback.print_exc()
974 return 0
975
976def infoCmd(ctx, args):
977 if len(args) < 2:
978 print("usage: info <vmname|uuid>")
979 return 0
980 mach = argsToMach(ctx, args)
981 if not mach:
982 return 0
983 try:
984 vmos = ctx['vb'].getGuestOSType(mach.OSTypeId)
985 except:
986 vmos = None
987 print(" One can use setvar <mach> <var> <value> to change variable, using name in [].")
988 print(" Name [name]: %s" % (colVm(ctx, mach.name)))
989 print(" Description [description]: %s" % (mach.description))
990 print(" ID [n/a]: %s" % (mach.id))
991 print(" OS Type [via OSTypeId]: %s" % (vmos.description if vmos is not None else mach.OSTypeId))
992 print(" Firmware [firmwareType]: %s (%s)" % (asEnumElem(ctx, "FirmwareType", mach.firmwareSettings.firmwareType), mach.firmwareSettings.firmwareType))
993 print()
994 print(" CPUs [CPUCount]: %d" % (mach.CPUCount))
995 print(" RAM [memorySize]: %dM" % (mach.memorySize))
996 print(" VRAM [VRAMSize]: %dM" % (mach.graphicsAdapter.VRAMSize))
997 print(" Monitors [monitorCount]: %d" % (mach.graphicsAdapter.monitorCount))
998 print(" Chipset [chipsetType]: %s (%s)" % (asEnumElem(ctx, "ChipsetType", mach.platform.chipsetType), mach.platform.chipsetType))
999 print()
1000 print(" Clipboard mode [clipboardMode]: %s (%s)" % (asEnumElem(ctx, "ClipboardMode", mach.clipboardMode), mach.clipboardMode))
1001 print(" Machine status [n/a]: %s (%s)" % (asEnumElem(ctx, "SessionState", mach.sessionState), mach.sessionState))
1002 print()
1003 if mach.teleporterEnabled:
1004 print(" Teleport target on port %d (%s)" % (mach.teleporterPort, mach.teleporterPassword))
1005 print()
1006 print(" ACPI [BIOSSettings.ACPIEnabled]: %s" % (asState(mach.firmwareSettings.ACPIEnabled)))
1007 print(" APIC [BIOSSettings.IOAPICEnabled]: %s" % (asState(mach.firmwareSettings.IOAPICEnabled)))
1008 if mach.platform.architecture == ctx['global'].constants.PlatformArchitecture_x86:
1009 hwVirtEnabled = mach.platform.x86.getHWVirtExProperty(ctx['global'].constants.HWVirtExPropertyType_Enabled)
1010 print(" Hardware virtualization [guest win machine.setHWVirtExProperty(ctx[\\'const\\'].HWVirtExPropertyType_Enabled, value)]: " + asState(hwVirtEnabled))
1011 hwVirtVPID = mach.platform.x86.getHWVirtExProperty(ctx['const'].HWVirtExPropertyType_VPID)
1012 print(" VPID support [guest win machine.setHWVirtExProperty(ctx[\\'const\\'].HWVirtExPropertyType_VPID, value)]: " + asState(hwVirtVPID))
1013 hwVirtNestedPaging = mach.platform.x86.getHWVirtExProperty(ctx['const'].HWVirtExPropertyType_NestedPaging)
1014 print(" Nested paging [guest win machine.setHWVirtExProperty(ctx[\\'const\\'].HWVirtExPropertyType_NestedPaging, value)]: " + asState(hwVirtNestedPaging))
1015 print(" HPET [HPETEnabled]: %s" % (asState(mach.platform.x86.HPETEnabled)))
1016
1017 print(" Hardware 3d acceleration [accelerate3DEnabled]: " + asState(mach.graphicsAdapter.isFeatureEnabled(ctx['const'].GraphicsFeature_Acceleration3D)))
1018 print(" Hardware 2d video acceleration [accelerate2DVideoEnabled]: " + asState(mach.graphicsAdapter.isFeatureEnabled(ctx['const'].GraphicsFeature_Acceleration2DVideo)))
1019 print(" Use universal time [RTCUseUTC]: %s" % (asState(mach.platform.RTCUseUTC)))
1020 audioAdp = mach.audioSettings.adapter
1021 if audioAdp.enabled:
1022 print(" Audio [via audioAdapter]: chip %s; host driver %s" % (asEnumElem(ctx, "AudioControllerType", audioAdp.audioController), asEnumElem(ctx, "AudioDriverType", audioAdp.audioDriver)))
1023 print(" CPU hotplugging [CPUHotPlugEnabled]: %s" % (asState(mach.CPUHotPlugEnabled)))
1024
1025 print(" Keyboard [keyboardHIDType]: %s (%s)" % (asEnumElem(ctx, "KeyboardHIDType", mach.keyboardHIDType), mach.keyboardHIDType))
1026 print(" Pointing device [pointingHIDType]: %s (%s)" % (asEnumElem(ctx, "PointingHIDType", mach.pointingHIDType), mach.pointingHIDType))
1027 print(" Last changed [n/a]: " + time.asctime(time.localtime(int(mach.lastStateChange)/1000)))
1028 # OSE has no VRDE
1029 try:
1030 print(" VRDE server [VRDEServer.enabled]: %s" % (asState(mach.VRDEServer.enabled)))
1031 except:
1032 pass
1033
1034 print()
1035 print(colCat(ctx, " USB Controllers:"))
1036 for oUsbCtrl in ctx['global'].getArray(mach, 'USBControllers'):
1037 print(" '%s': type %s standard: %#x" \
1038 % (oUsbCtrl.name, asEnumElem(ctx, "USBControllerType", oUsbCtrl.type), oUsbCtrl.USBStandard))
1039
1040 print()
1041 print(colCat(ctx, " I/O subsystem info:"))
1042 print(" Cache enabled [IOCacheEnabled]: %s" % (asState(mach.IOCacheEnabled)))
1043 print(" Cache size [IOCacheSize]: %dM" % (mach.IOCacheSize))
1044
1045 controllers = ctx['global'].getArray(mach, 'storageControllers')
1046 if controllers:
1047 print()
1048 print(colCat(ctx, " Storage Controllers:"))
1049 for controller in controllers:
1050 print(" '%s': bus %s type %s" % (controller.name, asEnumElem(ctx, "StorageBus", controller.bus), asEnumElem(ctx, "StorageControllerType", controller.controllerType)))
1051
1052 attaches = ctx['global'].getArray(mach, 'mediumAttachments')
1053 if attaches:
1054 print()
1055 print(colCat(ctx, " Media:"))
1056 for att in attaches:
1057 print(" Controller: '%s' port/device: %d:%d type: %s (%s):" % (att.controller, att.port, att.device, asEnumElem(ctx, "DeviceType", att.type), att.type))
1058 medium = att.medium
1059 if att.type == ctx['global'].constants.DeviceType_HardDisk:
1060 print(" HDD:")
1061 print(" Id: %s" % (medium.id))
1062 print(" Location: %s" % (colPath(ctx, medium.location)))
1063 print(" Name: %s" % (medium.name))
1064 print(" Format: %s" % (medium.format))
1065
1066 if att.type == ctx['global'].constants.DeviceType_DVD:
1067 print(" DVD:")
1068 if medium:
1069 print(" Id: %s" % (medium.id))
1070 print(" Name: %s" % (medium.name))
1071 if medium.hostDrive:
1072 print(" Host DVD %s" % (colPath(ctx, medium.location)))
1073 if att.passthrough:
1074 print(" [passthrough mode]")
1075 else:
1076 print(" Virtual image at %s" % (colPath(ctx, medium.location)))
1077 print(" Size: %s" % (medium.size))
1078
1079 if att.type == ctx['global'].constants.DeviceType_Floppy:
1080 print(" Floppy:")
1081 if medium:
1082 print(" Id: %s" % (medium.id))
1083 print(" Name: %s" % (medium.name))
1084 if medium.hostDrive:
1085 print(" Host floppy %s" % (colPath(ctx, medium.location)))
1086 else:
1087 print(" Virtual image at %s" % (colPath(ctx, medium.location)))
1088 print(" Size: %s" % (medium.size))
1089
1090 print()
1091 print(colCat(ctx, " Shared folders:"))
1092 for sharedfolder in ctx['global'].getArray(mach, 'sharedFolders'):
1093 printSf(ctx, sharedfolder)
1094
1095 return 0
1096
1097def startCmd(ctx, args):
1098 if len(args) < 2:
1099 print("usage: start <vmname|uuid> <frontend>")
1100 return 0
1101 mach = argsToMach(ctx, args)
1102 if not mach:
1103 return 0
1104 if len(args) > 2:
1105 vmtype = args[2]
1106 else:
1107 vmtype = "gui"
1108 startVm(ctx, mach, vmtype)
1109 return 0
1110
1111def createVmCmd(ctx, args):
1112 if len(args) != 4:
1113 print("usage: createvm <name> <arch> <ostype>")
1114 return 0
1115 name = args[1]
1116 arch = args[2]
1117 oskind = args[3]
1118 try:
1119 ctx['vb'].getGuestOSType(oskind)
1120 except Exception:
1121 print('Unknown OS type:', oskind)
1122 return 0
1123 createVm(ctx, name, arch, oskind)
1124 return 0
1125
1126def ginfoCmd(ctx, args):
1127 if len(args) < 2:
1128 print("usage: ginfo <vmname|uuid>")
1129 return 0
1130 mach = argsToMach(ctx, args)
1131 if not mach:
1132 return 0
1133 cmdExistingVm(ctx, mach, 'ginfo', '')
1134 return 0
1135
1136def gstctlPrintOk(_ctx, string):
1137 return print(colored(string, 'green'))
1138
1139def gstctlPrintErr(_ctx, string):
1140 return print(colored(string, 'red'))
1141
1142def execInGuest(ctx, console, args, env, user, passwd, tmo, inputPipe=None, _outputPipe=None):
1143 if len(args) < 1:
1144 print("exec in guest needs at least program name")
1145 return 1
1146 guest = console.guest
1147 # shall contain program name as argv[0]
1148 gargs = args
1149 if g_fVerbose:
1150 gstctlPrintOk(ctx, "starting guest session for user '%s' (password '%s')" % (user, passwd))
1151 else:
1152 gstctlPrintOk(ctx, ("starting guest session for user '%s' ..." % (user)))
1153 try:
1154 guestSession = guest.createSession(user, passwd, "", "vboxshell guest exec")
1155 guestSession.waitForArray([ ctx['global'].constants.GuestSessionWaitForFlag_Start ], 30 * 1000)
1156 except Exception as e:
1157 gstctlPrintErr(ctx, "starting guest session failed:")
1158 printErr(ctx, e)
1159 return 1
1160 if g_fVerbose:
1161 gstctlPrintOk(ctx, "guest session %d started" % guestSession.id)
1162 aProcCreateFlags = [ ctx['global'].constants.ProcessCreateFlag_WaitForStdOut, \
1163 ctx['global'].constants.ProcessCreateFlag_WaitForStdErr ]
1164 if inputPipe is not None:
1165 aProcCreateFlags.extend([ ctx['global'].constants.ProcessCreateFlag_WaitForStdIn ])
1166 if g_fVerbose:
1167 gstctlPrintOk(ctx, "starting process '%s' with args '%s' as user '%s' (password '%s')" % (args[0], gargs, user, passwd))
1168 process = guestSession.processCreate(args[0], gargs, '', env, aProcCreateFlags, tmo)
1169 try:
1170 waitResult = process.waitForArray([ ctx['global'].constants.ProcessWaitForFlag_Start ], 30 * 1000)
1171 except Exception as e:
1172 gstctlPrintErr(ctx, "waiting for guest process start failed:")
1173 printErr(ctx, e)
1174 return 1
1175 if waitResult != ctx['global'].constants.ProcessWaitResult_Start:
1176 gstctlPrintErr(ctx, "process start failed: got wait result %d, expected %d" \
1177 % (waitResult, ctx['global'].constants.ProcessWaitResult_Start) )
1178 return 1
1179 procStatus = process.status
1180 if procStatus != ctx['global'].constants.ProcessStatus_Started:
1181 gstctlPrintErr(ctx, "process start failed: got process status %d, expected %d" \
1182 % (procStatus, ctx['global'].constants.ProcessStatus_Started) )
1183 return 1
1184 if g_fVerbose:
1185 gstctlPrintOk(ctx, "process %d started" % (process.PID))
1186 if process.PID != 0:
1187 try:
1188 fCompleted = False
1189 fReadStdOut = False
1190 fReadStdErr = False
1191 while not fCompleted:
1192 waitResult = process.waitForArray([ ctx['global'].constants.ProcessWaitForFlag_Terminate, \
1193 ctx['global'].constants.ProcessWaitForFlag_StdOut, \
1194 ctx['global'].constants.ProcessWaitForFlag_StdErr ], 1000)
1195 if waitResult == ctx['global'].constants.ProcessWaitResult_WaitFlagNotSupported:
1196 fReadStdOut = True
1197 fReadStdErr = True
1198 elif waitResult == ctx['global'].constants.ProcessWaitResult_Terminate:
1199 fCompleted = True
1200 break
1201 elif waitResult == ctx['global'].constants.ProcessWaitResult_Timeout:
1202 gstctlPrintErr(ctx, "timeout while waiting for process")
1203 break
1204 else:
1205 gstctlPrintErr(ctx, "got unhandled wait result %d" % (waitResult))
1206 if inputPipe:
1207 indata = inputPipe(ctx)
1208 if indata is not None:
1209 write = len(indata)
1210 off = 0
1211 while write > 0:
1212 written = process.write(0, 10*1000, indata[off:])
1213 off = off + written
1214 write = write - written
1215 else:
1216 # EOF
1217 try:
1218 process.write(0, 10*1000, " ")
1219 except:
1220 pass
1221 if fReadStdOut:
1222 data = process.read(1, 64 * 1024, 10*1000)
1223 if data and len(data):
1224 sys.stdout.write(bytes(data).decode('utf-8'))
1225 fReadStdOut = False
1226 if fReadStdErr:
1227 data = process.read(2, 64 * 1024, 10*1000)
1228 if data and len(data):
1229 sys.stderr.write(bytes(data).decode('utf-8'))
1230 fReadStdErr = False
1231 ctx['global'].waitForEvents(0)
1232
1233 if fCompleted:
1234 exitCode = process.exitCode
1235 if exitCode == 0:
1236 gstctlPrintOk(ctx, "process exit code: %d" % (exitCode))
1237 else:
1238 gstctlPrintErr(ctx, "process exit code: %d" % (exitCode))
1239
1240 except KeyboardInterrupt:
1241 print("Interrupted.")
1242 ctx['interrupt'] = True
1243
1244 except Exception as e:
1245 printErr(ctx, e)
1246
1247 if guestSession:
1248 try:
1249 if g_fVerbose:
1250 gstctlPrintOk(ctx, "closing guest session ...")
1251 guestSession.close()
1252 except:
1253 printErr(ctx, e)
1254
1255 return 0
1256
1257
1258def copyToGuest(ctx, console, args, user, passwd):
1259 src = args[0]
1260 dst = args[1]
1261 flags = 0
1262 print("Copying host %s to guest %s" % (src, dst))
1263 progress = console.guest.copyToGuest(src, dst, user, passwd, flags)
1264 progressBar(ctx, progress)
1265
1266def nh_raw_input(prompt=""):
1267 if prompt:
1268 sys.stdout.write(prompt)
1269 sys.stdout.flush()
1270 line = sys.stdin.readline()
1271 if not line:
1272 raise EOFError
1273 if line[-1] == '\n':
1274 line = line[:-1]
1275 return line
1276
1277def getCred(_ctx):
1278 import getpass
1279 user = getpass.getuser()
1280 if user:
1281 user_inp = nh_raw_input("User (%s): " % (user))
1282 else:
1283 user_inp = nh_raw_input("User: ")
1284 if len(user_inp) > 0:
1285 user = user_inp
1286 passwd = getpass.getpass()
1287
1288 return (user, passwd)
1289
1290def gexecCmd(ctx, args):
1291 if len(args) < 2:
1292 print("usage: gexec <vmname|uuid> command args")
1293 return 0
1294 mach = argsToMach(ctx, args)
1295 if not mach:
1296 return 0
1297 gargs = args[2:]
1298 env = [] # ["DISPLAY=:0"]
1299 (user, passwd) = getCred(ctx)
1300 gargs.insert(0, lambda ctx, mach, console, args: execInGuest(ctx, console, args, env, user, passwd, 10000))
1301 cmdExistingVm(ctx, mach, 'guestlambda', gargs)
1302 return 0
1303
1304def gcopyCmd(ctx, args):
1305 if len(args) < 2:
1306 print("usage: gcopy <vmname|uuid> host_path guest_path")
1307 return 0
1308 mach = argsToMach(ctx, args)
1309 if not mach:
1310 return 0
1311 gargs = args[2:]
1312 (user, passwd) = getCred(ctx)
1313 gargs.insert(0, lambda ctx, mach, console, args: copyToGuest(ctx, console, args, user, passwd))
1314 cmdExistingVm(ctx, mach, 'guestlambda', gargs)
1315 return 0
1316
1317def readCmdPipe(ctx, _hcmd):
1318 try:
1319 return ctx['process'].communicate()[0]
1320 except:
1321 return None
1322
1323def gpipeCmd(ctx, args):
1324 if len(args) < 4:
1325 print("usage: gpipe <vmname|uuid> hostProgram guestProgram, such as gpipe linux '/bin/uname -a' '/bin/sh -c \"/usr/bin/tee; /bin/uname -a\"'")
1326 return 0
1327 mach = argsToMach(ctx, args)
1328 if not mach:
1329 return 0
1330 hcmd = args[2]
1331 gcmd = args[3]
1332 (user, passwd) = getCred(ctx)
1333 import subprocess
1334 with subprocess.Popen(split_no_quotes(hcmd), stdout=subprocess.PIPE) as ctx['process']:
1335 gargs = split_no_quotes(gcmd)
1336 env = []
1337 gargs.insert(0, lambda ctx, mach, console, args: execInGuest(ctx, console, args, env, user, passwd, 10000, lambda ctx:readCmdPipe(ctx, hcmd)))
1338 cmdExistingVm(ctx, mach, 'guestlambda', gargs)
1339 try:
1340 ctx['process'].terminate()
1341 except:
1342 pass
1343 ctx['process'] = None
1344 return 0
1345
1346
1347def removeVmCmd(ctx, args):
1348 mach = argsToMach(ctx, args)
1349 if not mach:
1350 return 0
1351 removeVm(ctx, mach)
1352 return 0
1353
1354def pauseCmd(ctx, args):
1355 mach = argsToMach(ctx, args)
1356 if not mach:
1357 return 0
1358 cmdExistingVm(ctx, mach, 'pause', '')
1359 return 0
1360
1361def powerdownCmd(ctx, args):
1362 mach = argsToMach(ctx, args)
1363 if not mach:
1364 return 0
1365 cmdExistingVm(ctx, mach, 'powerdown', '')
1366 return 0
1367
1368def powerbuttonCmd(ctx, args):
1369 mach = argsToMach(ctx, args)
1370 if not mach:
1371 return 0
1372 cmdExistingVm(ctx, mach, 'powerbutton', '')
1373 return 0
1374
1375def resumeCmd(ctx, args):
1376 mach = argsToMach(ctx, args)
1377 if not mach:
1378 return 0
1379 cmdExistingVm(ctx, mach, 'resume', '')
1380 return 0
1381
1382def saveCmd(ctx, args):
1383 mach = argsToMach(ctx, args)
1384 if not mach:
1385 return 0
1386 cmdExistingVm(ctx, mach, 'save', '')
1387 return 0
1388
1389def statsCmd(ctx, args):
1390 mach = argsToMach(ctx, args)
1391 if not mach:
1392 return 0
1393 cmdExistingVm(ctx, mach, 'stats', '')
1394 return 0
1395
1396def guestCmd(ctx, args):
1397 if len(args) < 3:
1398 print("usage: guest <vmname|uuid> commands")
1399 return 0
1400 mach = argsToMach(ctx, args)
1401 if not mach:
1402 return 0
1403 if mach.state != ctx['const'].MachineState_Running:
1404 cmdClosedVm(ctx, mach, lambda ctx, mach, a: guestExec (ctx, mach, None, ' '.join(args[2:])))
1405 else:
1406 cmdExistingVm(ctx, mach, 'guest', ' '.join(args[2:]))
1407 return 0
1408
1409def screenshotCmd(ctx, args):
1410 if len(args) < 2:
1411 print("usage: screenshot <vmname|uuid> <file> <width> <height> <monitor>")
1412 return 0
1413 mach = argsToMach(ctx, args)
1414 if not mach:
1415 return 0
1416 cmdExistingVm(ctx, mach, 'screenshot', args[2:])
1417 return 0
1418
1419def teleportCmd(ctx, args):
1420 if len(args) < 3:
1421 print("usage: teleport <vmname|uuid> host:port <password>")
1422 return 0
1423 mach = argsToMach(ctx, args)
1424 if not mach:
1425 return 0
1426 cmdExistingVm(ctx, mach, 'teleport', args[2:])
1427 return 0
1428
1429def portalsettings(_ctx, mach, args):
1430 enabled = args[0]
1431 mach.teleporterEnabled = enabled
1432 if enabled:
1433 port = args[1]
1434 passwd = args[2]
1435 mach.teleporterPort = port
1436 mach.teleporterPassword = passwd
1437
1438def openportalCmd(ctx, args):
1439 if len(args) < 3:
1440 print("usage: openportal <vmname|uuid> port <password>")
1441 return 0
1442 mach = argsToMach(ctx, args)
1443 if not mach:
1444 return 0
1445 port = int(args[2])
1446 if len(args) > 3:
1447 passwd = args[3]
1448 else:
1449 passwd = ""
1450 if not mach.teleporterEnabled or mach.teleporterPort != port or passwd:
1451 cmdClosedVm(ctx, mach, portalsettings, [True, port, passwd])
1452 startVm(ctx, mach, "gui")
1453 return 0
1454
1455def closeportalCmd(ctx, args):
1456 if len(args) < 2:
1457 print("usage: closeportal <vmname|uuid>")
1458 return 0
1459 mach = argsToMach(ctx, args)
1460 if not mach:
1461 return 0
1462 if mach.teleporterEnabled:
1463 cmdClosedVm(ctx, mach, portalsettings, [False])
1464 return 0
1465
1466def gueststatsCmd(ctx, args):
1467 if len(args) < 2:
1468 print("usage: gueststats <vmname|uuid> <check interval>")
1469 return 0
1470 mach = argsToMach(ctx, args)
1471 if not mach:
1472 return 0
1473 cmdExistingVm(ctx, mach, 'gueststats', args[2:])
1474 return 0
1475
1476def plugcpu(_ctx, mach, args):
1477 plug = args[0]
1478 cpu = args[1]
1479 if plug:
1480 print("Adding CPU %d..." % (cpu))
1481 mach.hotPlugCPU(cpu)
1482 else:
1483 print("Removing CPU %d..." % (cpu))
1484 mach.hotUnplugCPU(cpu)
1485
1486def plugcpuCmd(ctx, args):
1487 if len(args) < 2:
1488 print("usage: plugcpu <vmname|uuid> <cpuid>")
1489 return 0
1490 mach = argsToMach(ctx, args)
1491 if not mach:
1492 return 0
1493 if str(mach.sessionState) != str(ctx['const'].SessionState_Locked):
1494 if mach.CPUHotPlugEnabled:
1495 cmdClosedVm(ctx, mach, plugcpu, [True, int(args[2])])
1496 else:
1497 cmdExistingVm(ctx, mach, 'plugcpu', args[2])
1498 return 0
1499
1500def unplugcpuCmd(ctx, args):
1501 if len(args) < 2:
1502 print("usage: unplugcpu <vmname|uuid> <cpuid>")
1503 return 0
1504 mach = argsToMach(ctx, args)
1505 if not mach:
1506 return 0
1507 if str(mach.sessionState) != str(ctx['const'].SessionState_Locked):
1508 if mach.CPUHotPlugEnabled:
1509 cmdClosedVm(ctx, mach, plugcpu, [False, int(args[2])])
1510 else:
1511 cmdExistingVm(ctx, mach, 'unplugcpu', args[2])
1512 return 0
1513
1514def setvar(_ctx, _mach, args):
1515 expr = 'mach.'+args[0]+' = '+args[1]
1516 print("Executing", expr)
1517 exec(expr) # pylint: disable=exec-used
1518
1519def setvarCmd(ctx, args):
1520 if len(args) < 4:
1521 print("usage: setvar <vmname|uuid> <expr> <value>")
1522 return 0
1523 mach = argsToMach(ctx, args)
1524 if not mach:
1525 return 0
1526 cmdClosedVm(ctx, mach, setvar, args[2:])
1527 return 0
1528
1529def setvmextra(_ctx, mach, args):
1530 key = args[0]
1531 value = args[1]
1532 print("%s: setting %s to %s" % (mach.name, key, value if value else None))
1533 mach.setExtraData(key, value)
1534
1535def setExtraDataCmd(ctx, args):
1536 if len(args) < 3:
1537 print("usage: setextra [vmname|uuid|global] key <value>")
1538 return 0
1539 key = args[2]
1540 if len(args) == 4:
1541 value = args[3]
1542 else:
1543 value = ''
1544 if args[1] == 'global':
1545 ctx['vb'].setExtraData(key, value)
1546 return 0
1547
1548 mach = argsToMach(ctx, args)
1549 if not mach:
1550 return 0
1551 cmdClosedVm(ctx, mach, setvmextra, [key, value])
1552 return 0
1553
1554def printExtraKey(obj, key, value):
1555 print("%s: '%s' = '%s'" % (obj, key, value))
1556
1557def getExtraDataCmd(ctx, args):
1558 if len(args) < 2:
1559 print("usage: getextra [vmname|uuid|global] <key>")
1560 return 0
1561 if len(args) == 3:
1562 key = args[2]
1563 else:
1564 key = None
1565
1566 if args[1] == 'global':
1567 obj = ctx['vb']
1568 else:
1569 obj = argsToMach(ctx, args)
1570 if not obj:
1571 return 0
1572
1573 if not key:
1574 keys = obj.getExtraDataKeys()
1575 else:
1576 keys = [ key ]
1577 for k in keys:
1578 printExtraKey(args[1], k, obj.getExtraData(k))
1579
1580 return 0
1581
1582def quitCmd(_ctx, _args):
1583 return 1
1584
1585def aliasCmd(_ctx, args):
1586 if len(args) == 3:
1587 aliases[args[1]] = args[2]
1588 return 0
1589
1590 for (key, value) in list(aliases.items()):
1591 print("'%s' is an alias for '%s'" % (key, value))
1592 return 0
1593
1594def verboseCmd(_ctx, args):
1595 global g_fVerbose
1596 if len(args) > 1:
1597 g_fVerbose = args[1]=='on'
1598 else:
1599 g_fVerbose = not g_fVerbose
1600 return 0
1601
1602def colorsCmd(_ctx, args):
1603 global g_fHasColors
1604 if len(args) > 1:
1605 g_fHasColors = args[1] == 'on'
1606 else:
1607 g_fHasColors = not g_fHasColors
1608 return 0
1609
1610def hostCmd(ctx, _args):
1611 vbox = ctx['vb']
1612 try:
1613 print("VirtualBox version %s" % (colored(vbox.version, 'blue')))
1614 except Exception as e:
1615 printErr(ctx, e)
1616 if g_fVerbose:
1617 traceback.print_exc()
1618 props = vbox.systemProperties
1619 print("Machines: %s" % (colPath(ctx, props.defaultMachineFolder)))
1620
1621 #print("Global shared folders:")
1622 #for ud in ctx['global'].getArray(vbox, 'sharedFolders'):
1623 # printSf(ctx, sf)
1624 host = vbox.host
1625 cnt = host.processorCount
1626 print(colCat(ctx, "Processors:"))
1627 print(" available/online: %d/%d " % (cnt, host.processorOnlineCount))
1628 for i in range(0, cnt):
1629 print(" processor #%d speed: %dMHz %s" % (i, host.getProcessorSpeed(i), host.getProcessorDescription(i)))
1630
1631 print(colCat(ctx, "RAM:"))
1632 print(" %dM (free %dM)" % (host.memorySize, host.memoryAvailable))
1633 print(colCat(ctx, "OS:"))
1634 print(" %s (%s)" % (host.operatingSystem, host.OSVersion))
1635
1636 print(colCat(ctx, "Network interfaces:"))
1637 for iface in ctx['global'].getArray(host, 'networkInterfaces'):
1638 print(" %s (%s)" % (iface.name, iface.IPAddress))
1639
1640 print(colCat(ctx, "DVD drives:"))
1641 for drive in ctx['global'].getArray(host, 'DVDDrives'):
1642 print(" %s - %s" % (drive.name, drive.description))
1643
1644 print(colCat(ctx, "Floppy drives:"))
1645 for drive in ctx['global'].getArray(host, 'floppyDrives'):
1646 print(" %s - %s" % (drive.name, drive.description))
1647
1648 print(colCat(ctx, "USB devices:"))
1649 for usbdev in ctx['global'].getArray(host, 'USBDevices'):
1650 printHostUsbDev(ctx, usbdev)
1651
1652 if ctx['perf']:
1653 for metric in ctx['perf'].query(["*"], [host]):
1654 print(metric['name'], metric['values_as_string'])
1655
1656 return 0
1657
1658def monitorGuestCmd(ctx, args):
1659 if len(args) < 2:
1660 print("usage: monitorGuest <vmname|uuid> (duration)")
1661 return 0
1662 mach = argsToMach(ctx, args)
1663 if not mach:
1664 return 0
1665 dur = 5
1666 if len(args) > 2:
1667 dur = float(args[2])
1668 active = False
1669 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.eventSource, active, dur)])
1670 return 0
1671
1672def monitorGuestKbdCmd(ctx, args):
1673 if len(args) < 2:
1674 print("usage: monitorGuestKbd name (duration)")
1675 return 0
1676 mach = argsToMach(ctx, args)
1677 if not mach:
1678 return 0
1679 dur = 5
1680 if len(args) > 2:
1681 dur = float(args[2])
1682 active = False
1683 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.keyboard.eventSource, active, dur)])
1684 return 0
1685
1686def monitorGuestMouseCmd(ctx, args):
1687 if len(args) < 2:
1688 print("usage: monitorGuestMouse name (duration)")
1689 return 0
1690 mach = argsToMach(ctx, args)
1691 if not mach:
1692 return 0
1693 dur = 5
1694 if len(args) > 2:
1695 dur = float(args[2])
1696 active = False
1697 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.mouse.eventSource, active, dur)])
1698 return 0
1699
1700def monitorGuestMultiTouchCmd(ctx, args):
1701 if len(args) < 2:
1702 print("usage: monitorGuestMultiTouch name (duration)")
1703 return 0
1704 mach = argsToMach(ctx, args)
1705 if not mach:
1706 return 0
1707 dur = 5
1708 if len(args) > 2:
1709 dur = float(args[2])
1710 active = False
1711 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.mouse.eventSource, active, dur)])
1712 return 0
1713
1714def monitorVBoxCmd(ctx, args):
1715 if len(args) > 2:
1716 print("usage: monitorVBox (duration)")
1717 return 0
1718 dur = 5
1719 if len(args) > 1:
1720 dur = float(args[1])
1721 vbox = ctx['vb']
1722 active = False
1723 monitorSource(ctx, vbox.eventSource, active, dur)
1724 return 0
1725
1726def getAdapterType(ctx, natype):
1727 if (natype in ( ctx['global'].constants.NetworkAdapterType_Am79C970A
1728 , ctx['global'].constants.NetworkAdapterType_Am79C973
1729 , ctx['global'].constants.NetworkAdapterType_Am79C960)):
1730 return "pcnet"
1731 if (natype in ( ctx['global'].constants.NetworkAdapterType_I82540EM
1732 , ctx['global'].constants.NetworkAdapterType_I82545EM
1733 , ctx['global'].constants.NetworkAdapterType_I82543GC)):
1734 return "e1000"
1735 if natype == ctx['global'].constants.NetworkAdapterType_Virtio:
1736 return "virtio"
1737 if natype == ctx['global'].constants.NetworkAdapterType_Null:
1738 return None
1739 raise Exception("Unknown adapter type: "+natype)
1740
1741def portForwardCmd(ctx, args):
1742 if len(args) != 5:
1743 print("usage: portForward <vmname|uuid> <adapter> <hostPort> <guestPort>")
1744 return 0
1745 mach = argsToMach(ctx, args)
1746 if not mach:
1747 return 0
1748 adapterNum = int(args[2])
1749 hostPort = int(args[3])
1750 guestPort = int(args[4])
1751 proto = "TCP"
1752 session = ctx['global'].openMachineSession(mach, fPermitSharing=True)
1753 mach = session.machine
1754
1755 adapter = mach.getNetworkAdapter(adapterNum)
1756 adapterType = getAdapterType(ctx, adapter.adapterType)
1757
1758 profile_name = proto+"_"+str(hostPort)+"_"+str(guestPort)
1759 config = "VBoxInternal/Devices/" + adapterType + "/"
1760 config = config + str(adapter.slot) +"/LUN#0/Config/" + profile_name
1761
1762 mach.setExtraData(config + "/Protocol", proto)
1763 mach.setExtraData(config + "/HostPort", str(hostPort))
1764 mach.setExtraData(config + "/GuestPort", str(guestPort))
1765
1766 mach.saveSettings()
1767 session.unlockMachine()
1768
1769 return 0
1770
1771
1772def showLogCmd(ctx, args):
1773 if len(args) < 2:
1774 print("usage: showLog <vmname|uuid> <num>")
1775 return 0
1776 mach = argsToMach(ctx, args)
1777 if not mach:
1778 return 0
1779
1780 log = 0
1781 if len(args) > 2:
1782 log = args[2]
1783
1784 uOffset = 0
1785 while True:
1786 data = mach.readLog(log, uOffset, 4096)
1787 if len(data) == 0:
1788 break
1789 # print adds either NL or space to chunks not ending with a NL
1790 sys.stdout.write(str(data))
1791 uOffset += len(data)
1792
1793 return 0
1794
1795def findLogCmd(ctx, args):
1796 if len(args) < 3:
1797 print("usage: findLog <vmname|uuid> <pattern> <num>")
1798 return 0
1799 mach = argsToMach(ctx, args)
1800 if not mach:
1801 return 0
1802
1803 log = 0
1804 if len(args) > 3:
1805 log = args[3]
1806
1807 pattern = args[2]
1808 uOffset = 0
1809 while True:
1810 # to reduce line splits on buffer boundary
1811 data = mach.readLog(log, uOffset, 512*1024)
1812 if len(data) == 0:
1813 break
1814 buf = str(data).split("\n")
1815 for line in buf:
1816 match = re.findall(pattern, line)
1817 if len(match) > 0:
1818 for cur_match in match:
1819 line = line.replace(cur_match, colored(cur_match, 'red'))
1820 print(line)
1821 uOffset += len(data)
1822
1823 return 0
1824
1825
1826def findAssertCmd(ctx, args):
1827 if len(args) < 2:
1828 print("usage: findAssert <vmname|uuid> <num>")
1829 return 0
1830 mach = argsToMach(ctx, args)
1831 if not mach:
1832 return 0
1833
1834 log = 0
1835 if len(args) > 2:
1836 log = args[2]
1837
1838 uOffset = 0
1839 ere = re.compile(r'(Expression:|\!\!\!\!\!\!)')
1840 active = False
1841 context = 0
1842 while True:
1843 # to reduce line splits on buffer boundary
1844 data = mach.readLog(log, uOffset, 512*1024)
1845 if len(data) == 0:
1846 break
1847 buf = str(data).split("\n")
1848 for line in buf:
1849 if active:
1850 print(line)
1851 if context == 0:
1852 active = False
1853 else:
1854 context = context - 1
1855 continue
1856 match = ere.findall(line)
1857 if len(match) > 0:
1858 active = True
1859 context = 50
1860 print(line)
1861 uOffset += len(data)
1862
1863 return 0
1864
1865def evalCmd(ctx, args):
1866 expr = ' '.join(args[1:])
1867 try:
1868 exec(expr) # pylint: disable=exec-used
1869 except Exception as e:
1870 printErr(ctx, e)
1871 if g_fVerbose:
1872 traceback.print_exc()
1873 return 0
1874
1875def reloadExtCmd(ctx, _args):
1876 # maybe will want more args smartness
1877 checkUserExtensions(ctx, commands, getHomeFolder(ctx))
1878 autoCompletion(commands, ctx)
1879 return 0
1880
1881def runScriptCmd(ctx, args):
1882 if len(args) != 2:
1883 print("usage: runScript <script>")
1884 return 0
1885
1886 try:
1887 with open(args[1], 'r', encoding='utf-8') as file:
1888 try:
1889 lines = file.readlines()
1890 ctx['scriptLine'] = 0
1891 ctx['interrupt'] = False
1892 while ctx['scriptLine'] < len(lines):
1893 line = lines[ctx['scriptLine']]
1894 ctx['scriptLine'] = ctx['scriptLine'] + 1
1895 done = runCommand(ctx, line)
1896 if done != 0 or ctx['interrupt']:
1897 break
1898
1899 except Exception as e:
1900 printErr(ctx, e)
1901 if g_fVerbose:
1902 traceback.print_exc()
1903 file.close()
1904 except IOError as e:
1905 print("cannot open:", args[1], ":", e)
1906 return 1
1907 return 0
1908
1909def sleepCmd(_ctx, args):
1910 if len(args) != 2:
1911 print("usage: sleep <secs>")
1912 return 0
1913
1914 try:
1915 time.sleep(float(args[1]))
1916 except:
1917 # to allow sleep interrupt
1918 pass
1919 return 0
1920
1921
1922def shellCmd(_ctx, args):
1923 if len(args) < 2:
1924 print("usage: shell <commands>")
1925 return 0
1926 cmd = ' '.join(args[1:])
1927
1928 try:
1929 os.system(cmd)
1930 except KeyboardInterrupt:
1931 # to allow shell command interruption
1932 pass
1933 return 0
1934
1935
1936def connectCmd(ctx, args):
1937 if len(args) > 4:
1938 print("usage: connect url <username> <passwd>")
1939 return 0
1940
1941 if ctx['vb'] is not None:
1942 print("Already connected, disconnect first...")
1943 return 0
1944
1945 if len(args) > 1:
1946 url = args[1]
1947 else:
1948 url = None
1949
1950 if len(args) > 2:
1951 user = args[2]
1952 else:
1953 user = ""
1954
1955 if len(args) > 3:
1956 passwd = args[3]
1957 else:
1958 passwd = ""
1959
1960 ctx['wsinfo'] = [url, user, passwd]
1961 ctx['vb'] = ctx['global'].platform.connect(url, user, passwd)
1962 try:
1963 print("Running VirtualBox version %s" % (ctx['vb'].version))
1964 except Exception as e:
1965 printErr(ctx, e)
1966 if g_fVerbose:
1967 traceback.print_exc()
1968 ctx['perf'] = ctx['global'].getPerfCollector(ctx['vb'])
1969 return 0
1970
1971def disconnectCmd(ctx, args):
1972 if len(args) != 1:
1973 print("usage: disconnect")
1974 return 0
1975
1976 if ctx['vb'] is None:
1977 print("Not connected yet.")
1978 return 0
1979
1980 try:
1981 ctx['global'].platform.disconnect()
1982 except:
1983 ctx['vb'] = None
1984 raise
1985
1986 ctx['vb'] = None
1987 return 0
1988
1989def reconnectCmd(ctx, _args):
1990 if ctx['wsinfo'] is None:
1991 print("Never connected...")
1992 return 0
1993
1994 try:
1995 ctx['global'].platform.disconnect()
1996 except:
1997 pass
1998
1999 [url, user, passwd] = ctx['wsinfo']
2000 ctx['vb'] = ctx['global'].platform.connect(url, user, passwd)
2001 try:
2002 print("Running VirtualBox version %s" % (ctx['vb'].version))
2003 except Exception as e:
2004 printErr(ctx, e)
2005 if g_fVerbose:
2006 traceback.print_exc()
2007 ctx['perf'] = ctx['global'].getPerfCollector(ctx['vb'])
2008 return 0
2009
2010def exportVMCmd(ctx, args):
2011 if len(args) < 3:
2012 print("usage: exportVm <machine> <path> <format> <license>")
2013 return 0
2014 mach = argsToMach(ctx, args)
2015 if mach is None:
2016 return 0
2017 path = args[2]
2018 if len(args) > 3:
2019 fmt = args[3]
2020 else:
2021 fmt = "ovf-1.0"
2022 if len(args) > 4:
2023 lic = args[4]
2024 else:
2025 lic = "GPL"
2026
2027 app = ctx['vb'].createAppliance()
2028 desc = mach.export(app)
2029 desc.addDescription(ctx['global'].constants.VirtualSystemDescriptionType_License, lic, "")
2030 progress = app.write(fmt, path)
2031 if (progressBar(ctx, progress) and int(progress.resultCode) == 0):
2032 print("Exported to %s in format %s" % (path, fmt))
2033 else:
2034 reportError(ctx, progress)
2035 return 0
2036
2037# PC XT scancodes
2038scancodes = {
2039 'a': 0x1e,
2040 'b': 0x30,
2041 'c': 0x2e,
2042 'd': 0x20,
2043 'e': 0x12,
2044 'f': 0x21,
2045 'g': 0x22,
2046 'h': 0x23,
2047 'i': 0x17,
2048 'j': 0x24,
2049 'k': 0x25,
2050 'l': 0x26,
2051 'm': 0x32,
2052 'n': 0x31,
2053 'o': 0x18,
2054 'p': 0x19,
2055 'q': 0x10,
2056 'r': 0x13,
2057 's': 0x1f,
2058 't': 0x14,
2059 'u': 0x16,
2060 'v': 0x2f,
2061 'w': 0x11,
2062 'x': 0x2d,
2063 'y': 0x15,
2064 'z': 0x2c,
2065 '0': 0x0b,
2066 '1': 0x02,
2067 '2': 0x03,
2068 '3': 0x04,
2069 '4': 0x05,
2070 '5': 0x06,
2071 '6': 0x07,
2072 '7': 0x08,
2073 '8': 0x09,
2074 '9': 0x0a,
2075 ' ': 0x39,
2076 '-': 0xc,
2077 '=': 0xd,
2078 '[': 0x1a,
2079 ']': 0x1b,
2080 ';': 0x27,
2081 '\'': 0x28,
2082 ',': 0x33,
2083 '.': 0x34,
2084 '/': 0x35,
2085 '\t': 0xf,
2086 '\n': 0x1c,
2087 '`': 0x29
2088}
2089
2090extScancodes = {
2091 'ESC' : [0x01],
2092 'BKSP': [0xe],
2093 'SPACE': [0x39],
2094 'TAB': [0x0f],
2095 'CAPS': [0x3a],
2096 'ENTER': [0x1c],
2097 'LSHIFT': [0x2a],
2098 'RSHIFT': [0x36],
2099 'INS': [0xe0, 0x52],
2100 'DEL': [0xe0, 0x53],
2101 'END': [0xe0, 0x4f],
2102 'HOME': [0xe0, 0x47],
2103 'PGUP': [0xe0, 0x49],
2104 'PGDOWN': [0xe0, 0x51],
2105 'LGUI': [0xe0, 0x5b], # GUI, aka Win, aka Apple key
2106 'RGUI': [0xe0, 0x5c],
2107 'LCTR': [0x1d],
2108 'RCTR': [0xe0, 0x1d],
2109 'LALT': [0x38],
2110 'RALT': [0xe0, 0x38],
2111 'APPS': [0xe0, 0x5d],
2112 'F1': [0x3b],
2113 'F2': [0x3c],
2114 'F3': [0x3d],
2115 'F4': [0x3e],
2116 'F5': [0x3f],
2117 'F6': [0x40],
2118 'F7': [0x41],
2119 'F8': [0x42],
2120 'F9': [0x43],
2121 'F10': [0x44 ],
2122 'F11': [0x57],
2123 'F12': [0x58],
2124 'UP': [0xe0, 0x48],
2125 'LEFT': [0xe0, 0x4b],
2126 'DOWN': [0xe0, 0x50],
2127 'RIGHT': [0xe0, 0x4d],
2128}
2129
2130def keyDown(ch):
2131 code = scancodes.get(ch, 0x0)
2132 if code != 0:
2133 return [code]
2134 extCode = extScancodes.get(ch, [])
2135 if len(extCode) == 0:
2136 print("bad ext", ch)
2137 return extCode
2138
2139def keyUp(ch):
2140 codes = keyDown(ch)[:] # make a copy
2141 if len(codes) > 0:
2142 codes[len(codes)-1] += 0x80
2143 return codes
2144
2145def typeInGuest(console, text, delay):
2146 pressed = []
2147 group = False
2148 modGroupEnd = True
2149 i = 0
2150 kbd = console.keyboard
2151 while i < len(text):
2152 ch = text[i]
2153 i = i+1
2154 if ch == '{':
2155 # start group, all keys to be pressed at the same time
2156 group = True
2157 continue
2158 if ch == '}':
2159 # end group, release all keys
2160 for c in pressed:
2161 kbd.putScancodes(keyUp(c))
2162 pressed = []
2163 group = False
2164 continue
2165 if ch == 'W':
2166 # just wait a bit
2167 time.sleep(0.3)
2168 continue
2169 if ch in ('^', '|', '$', '_'):
2170 if ch == '^':
2171 ch = 'LCTR'
2172 if ch == '|':
2173 ch = 'LSHIFT'
2174 if ch == '_':
2175 ch = 'LALT'
2176 if ch == '$':
2177 ch = 'LGUI'
2178 if not group:
2179 modGroupEnd = False
2180 else:
2181 if ch == '\\':
2182 if i < len(text):
2183 ch = text[i]
2184 i = i+1
2185 if ch == 'n':
2186 ch = '\n'
2187 elif ch == '&':
2188 combo = ""
2189 while i < len(text):
2190 ch = text[i]
2191 i = i+1
2192 if ch == ';':
2193 break
2194 combo += ch
2195 ch = combo
2196 modGroupEnd = True
2197 kbd.putScancodes(keyDown(ch))
2198 pressed.insert(0, ch)
2199 if not group and modGroupEnd:
2200 for c in pressed:
2201 kbd.putScancodes(keyUp(c))
2202 pressed = []
2203 modGroupEnd = True
2204 time.sleep(delay)
2205
2206def typeGuestCmd(ctx, args):
2207 if len(args) < 3:
2208 print("usage: typeGuest <machine> <text> <charDelay>")
2209 return 0
2210 mach = argsToMach(ctx, args)
2211 if mach is None:
2212 return 0
2213
2214 text = args[2]
2215
2216 if len(args) > 3:
2217 delay = float(args[3])
2218 else:
2219 delay = 0.1
2220
2221 gargs = [lambda ctx, mach, console, args: typeInGuest(console, text, delay)]
2222 cmdExistingVm(ctx, mach, 'guestlambda', gargs)
2223
2224 return 0
2225
2226def optId(verbose, uuid):
2227 if verbose:
2228 return ": "+uuid
2229 return ""
2230
2231def asSize(val, inBytes):
2232 if inBytes:
2233 return int(val)/(1024*1024)
2234 return int(val)
2235
2236def listMediaCmd(ctx, args):
2237 if len(args) > 1:
2238 verbose = int(args[1])
2239 else:
2240 verbose = False
2241 hdds = ctx['global'].getArray(ctx['vb'], 'hardDisks')
2242 print(colCat(ctx, "Hard disks:"))
2243 for hdd in hdds:
2244 if hdd.state != ctx['global'].constants.MediumState_Created:
2245 hdd.refreshState()
2246 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))))
2247
2248 dvds = ctx['global'].getArray(ctx['vb'], 'DVDImages')
2249 print(colCat(ctx, "CD/DVD disks:"))
2250 for dvd in dvds:
2251 if dvd.state != ctx['global'].constants.MediumState_Created:
2252 dvd.refreshState()
2253 print(" %s (%s)%s %s" % (colPath(ctx, dvd.location), dvd.format, optId(verbose, dvd.id), colSizeM(ctx, asSize(dvd.size, True))))
2254
2255 floppys = ctx['global'].getArray(ctx['vb'], 'floppyImages')
2256 print(colCat(ctx, "Floppy disks:"))
2257 for floppy in floppys:
2258 if floppy.state != ctx['global'].constants.MediumState_Created:
2259 floppy.refreshState()
2260 print(" %s (%s)%s %s" % (colPath(ctx, floppy.location), floppy.format, optId(verbose, floppy.id), colSizeM(ctx, asSize(floppy.size, True))))
2261
2262 return 0
2263
2264def listUsbCmd(ctx, args):
2265 if len(args) > 1:
2266 print("usage: listUsb")
2267 return 0
2268
2269 host = ctx['vb'].host
2270 for usbdev in ctx['global'].getArray(host, 'USBDevices'):
2271 printHostUsbDev(ctx, usbdev)
2272
2273 return 0
2274
2275def findDevOfType(ctx, mach, devtype):
2276 attachments = ctx['global'].getArray(mach, 'mediumAttachments')
2277 for att in attachments:
2278 if att.type == devtype:
2279 return [att.controller, att.port, att.device]
2280 return [None, 0, 0]
2281
2282def createHddCmd(ctx, args):
2283 if len(args) < 3:
2284 print("usage: createHdd sizeM location type")
2285 return 0
2286
2287 size = int(args[1])
2288 loc = args[2]
2289 if len(args) > 3:
2290 fmt = args[3]
2291 else:
2292 fmt = "vdi"
2293
2294 hdd = ctx['vb'].createMedium(fmt, loc, ctx['global'].constants.AccessMode_ReadWrite, ctx['global'].constants.DeviceType_HardDisk)
2295 progress = hdd.createBaseStorage(size, (ctx['global'].constants.MediumVariant_Standard, ))
2296 if progressBar(ctx,progress) and hdd.id:
2297 print("created HDD at %s as %s" % (colPath(ctx,hdd.location), hdd.id))
2298 else:
2299 print("cannot create disk (file %s exist?)" % (loc))
2300 reportError(ctx,progress)
2301 return 0
2302
2303 return 0
2304
2305def registerHddCmd(ctx, args):
2306 if len(args) < 2:
2307 print("usage: registerHdd location")
2308 return 0
2309
2310 vbox = ctx['vb']
2311 loc = args[1]
2312 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2313 print("registered HDD as %s" % (hdd.id))
2314 return 0
2315
2316def controldevice(_ctx, mach, args):
2317 [ctr, port, slot, devtype, uuid] = args
2318 mach.attachDevice(ctr, port, slot, devtype, uuid)
2319
2320def attachHddCmd(ctx, args):
2321 if len(args) < 3:
2322 print("usage: attachHdd <vmname|uuid> <hdd> <controller> <port:slot>")
2323 return 0
2324
2325 mach = argsToMach(ctx, args)
2326 if mach is None:
2327 return 0
2328 vbox = ctx['vb']
2329 loc = args[2]
2330 try:
2331 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2332 except:
2333 print("no HDD with path %s registered" % (loc))
2334 return 0
2335 if len(args) > 3:
2336 ctr = args[3]
2337 (port, slot) = args[4].split(":")
2338 else:
2339 [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_HardDisk)
2340
2341 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.attachDevice(ctr, port, slot, ctx['global'].constants.DeviceType_HardDisk, hdd.id))
2342 return 0
2343
2344def detachVmDevice(ctx, mach, args):
2345 attachments = ctx['global'].getArray(mach, 'mediumAttachments')
2346 hid = args[0]
2347 for att in attachments:
2348 if att.medium:
2349 if hid in ('ALL', att.medium.id):
2350 mach.detachDevice(att.controller, att.port, att.device)
2351
2352def detachMedium(ctx, mid, medium):
2353 cmdClosedVm(ctx, machById(ctx, mid), detachVmDevice, [medium])
2354
2355def detachHddCmd(ctx, args):
2356 if len(args) < 3:
2357 print("usage: detachHdd <vmname|uuid> <hdd>")
2358 return 0
2359
2360 mach = argsToMach(ctx, args)
2361 if mach is None:
2362 return 0
2363 vbox = ctx['vb']
2364 loc = args[2]
2365 try:
2366 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2367 except:
2368 print("no HDD with path %s registered" % (loc))
2369 return 0
2370
2371 detachMedium(ctx, mach.id, hdd)
2372 return 0
2373
2374def unregisterHddCmd(ctx, args):
2375 if len(args) < 2:
2376 print("usage: unregisterHdd path <vmunreg>")
2377 return 0
2378
2379 vbox = ctx['vb']
2380 loc = args[1]
2381 if len(args) > 2:
2382 vmunreg = int(args[2])
2383 else:
2384 vmunreg = 0
2385 try:
2386 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2387 except:
2388 print("no HDD with path %s registered" % (loc))
2389 return 0
2390
2391 if vmunreg != 0:
2392 machs = ctx['global'].getArray(hdd, 'machineIds')
2393 try:
2394 for mach in machs:
2395 print("Trying to detach from %s" % (mach))
2396 detachMedium(ctx, mach, hdd)
2397 except Exception as e:
2398 print('failed: ', e)
2399 return 0
2400 hdd.close()
2401 return 0
2402
2403def removeHddCmd(ctx, args):
2404 if len(args) != 2:
2405 print("usage: removeHdd path")
2406 return 0
2407
2408 vbox = ctx['vb']
2409 loc = args[1]
2410 try:
2411 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2412 except:
2413 print("no HDD with path %s registered" % (loc))
2414 return 0
2415
2416 progress = hdd.deleteStorage()
2417 progressBar(ctx, progress)
2418
2419 return 0
2420
2421def registerIsoCmd(ctx, args):
2422 if len(args) < 2:
2423 print("usage: registerIso location")
2424 return 0
2425
2426 vbox = ctx['vb']
2427 loc = args[1]
2428 iso = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2429 print("registered ISO as %s" % (iso.id))
2430 return 0
2431
2432def unregisterIsoCmd(ctx, args):
2433 if len(args) != 2:
2434 print("usage: unregisterIso path")
2435 return 0
2436
2437 vbox = ctx['vb']
2438 loc = args[1]
2439 try:
2440 vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2441 except:
2442 print("no DVD with path %s registered" % (loc))
2443 return 0
2444
2445 print("Unregistered ISO at %s" % (colPath(ctx, loc)))
2446 return 0
2447
2448def removeIsoCmd(ctx, args):
2449 if len(args) != 2:
2450 print("usage: removeIso path")
2451 return 0
2452
2453 vbox = ctx['vb']
2454 loc = args[1]
2455 try:
2456 dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2457 except:
2458 print("no DVD with path %s registered" % (loc))
2459 return 0
2460
2461 progress = dvd.deleteStorage()
2462 if progressBar(ctx, progress):
2463 print("Removed ISO at %s" % (colPath(ctx, dvd.location)))
2464 else:
2465 reportError(ctx, progress)
2466 return 0
2467
2468def attachIsoCmd(ctx, args):
2469 if len(args) < 3:
2470 print("usage: attachIso <vmname|uuid> <iso> <controller> <port:slot>")
2471 return 0
2472
2473 mach = argsToMach(ctx, args)
2474 if mach is None:
2475 return 0
2476 vbox = ctx['vb']
2477 loc = args[2]
2478 try:
2479 dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2480 except:
2481 print("no DVD with path %s registered" % (loc))
2482 return 0
2483 if len(args) > 3:
2484 ctr = args[3]
2485 (port, slot) = args[4].split(":")
2486 else:
2487 [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_DVD)
2488 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.attachDevice(ctr, port, slot, ctx['global'].constants.DeviceType_DVD, dvd))
2489 return 0
2490
2491def detachIsoCmd(ctx, args):
2492 if len(args) < 3:
2493 print("usage: detachIso <vmname|uuid> <iso>")
2494 return 0
2495
2496 mach = argsToMach(ctx, args)
2497 if mach is None:
2498 return 0
2499 vbox = ctx['vb']
2500 loc = args[2]
2501 try:
2502 dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2503 except:
2504 print("no DVD with path %s registered" % (loc))
2505 return 0
2506
2507 detachMedium(ctx, mach.id, dvd)
2508 return 0
2509
2510def mountIsoCmd(ctx, args):
2511 if len(args) < 3:
2512 print("usage: mountIso <vmname|uuid> <iso> <controller> <port:slot>")
2513 return 0
2514
2515 mach = argsToMach(ctx, args)
2516 if mach is None:
2517 return 0
2518 vbox = ctx['vb']
2519 loc = args[2]
2520 try:
2521 dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2522 except:
2523 print("no DVD with path %s registered" % (loc))
2524 return 0
2525
2526 if len(args) > 3:
2527 ctr = args[3]
2528 (port, slot) = args[4].split(":")
2529 else:
2530 # autodetect controller and location, just find first controller with media == DVD
2531 [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_DVD)
2532
2533 cmdExistingVm(ctx, mach, 'mountiso', [ctr, port, slot, dvd, True])
2534
2535 return 0
2536
2537def unmountIsoCmd(ctx, args):
2538 if len(args) < 2:
2539 print("usage: unmountIso <vmname|uuid> <controller> <port:slot>")
2540 return 0
2541
2542 mach = argsToMach(ctx, args)
2543 if mach is None:
2544 return 0
2545
2546 if len(args) > 3:
2547 ctr = args[2]
2548 (port, slot) = args[3].split(":")
2549 else:
2550 # autodetect controller and location, just find first controller with media == DVD
2551 [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_DVD)
2552
2553 cmdExistingVm(ctx, mach, 'mountiso', [ctr, port, slot, None, True])
2554
2555 return 0
2556
2557def attachCtr(_ctx, mach, args):
2558 [name, bus, ctrltype] = args
2559 ctr = mach.addStorageController(name, bus)
2560 if ctrltype:
2561 ctr.controllerType = ctrltype
2562
2563def attachCtrCmd(ctx, args):
2564 if len(args) < 4:
2565 print("usage: attachCtr <vmname|uuid> <controller name> <bus> <type>")
2566 return 0
2567
2568 if len(args) > 4:
2569 ctrltype = enumFromString(ctx, 'StorageControllerType', args[4])
2570 if not ctrltype:
2571 print("Controller type %s unknown" % (args[4]))
2572 return 0
2573 else:
2574 ctrltype = None
2575
2576 mach = argsToMach(ctx, args)
2577 if mach is None:
2578 return 0
2579 bus = enumFromString(ctx, 'StorageBus', args[3])
2580 if bus is None:
2581 print("Bus type %s unknown" % (args[3]))
2582 return 0
2583 name = args[2]
2584 cmdClosedVm(ctx, mach, attachCtr, [name, bus, ctrltype])
2585 return 0
2586
2587def detachCtrCmd(ctx, args):
2588 if len(args) < 3:
2589 print("usage: detachCtr <vmname|uuid> <controller name>")
2590 return 0
2591
2592 mach = argsToMach(ctx, args)
2593 if mach is None:
2594 return 0
2595 ctr = args[2]
2596 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.removeStorageController(ctr))
2597 return 0
2598
2599def usbctr(_ctx, _mach, console, args):
2600 if args[0]:
2601 console.attachUSBDevice(args[1], "")
2602 else:
2603 console.detachUSBDevice(args[1])
2604
2605def attachUsbCmd(ctx, args):
2606 if len(args) < 3:
2607 print("usage: attachUsb <vmname|uuid> <device uid>")
2608 return 0
2609
2610 mach = argsToMach(ctx, args)
2611 if mach is None:
2612 return 0
2613 dev = args[2]
2614 cmdExistingVm(ctx, mach, 'guestlambda', [usbctr, True, dev])
2615 return 0
2616
2617def detachUsbCmd(ctx, args):
2618 if len(args) < 3:
2619 print("usage: detachUsb <vmname|uuid> <device uid>")
2620 return 0
2621
2622 mach = argsToMach(ctx, args)
2623 if mach is None:
2624 return 0
2625 dev = args[2]
2626 cmdExistingVm(ctx, mach, 'guestlambda', [usbctr, False, dev])
2627 return 0
2628
2629
2630def guiCmd(ctx, args):
2631 if len(args) > 1:
2632 print("usage: gui")
2633 return 0
2634
2635 binDir = ctx['global'].getBinDir()
2636
2637 vbox = os.path.join(binDir, 'VirtualBox')
2638 try:
2639 os.system(vbox)
2640 except KeyboardInterrupt:
2641 # to allow interruption
2642 pass
2643 return 0
2644
2645def shareFolderCmd(ctx, args):
2646 if len(args) < 4:
2647 print("usage: shareFolder <vmname|uuid> <path> <name> <writable|persistent>")
2648 return 0
2649
2650 mach = argsToMach(ctx, args)
2651 if mach is None:
2652 return 0
2653 path = args[2]
2654 name = args[3]
2655 writable = False
2656 persistent = False
2657 if len(args) > 4:
2658 for cur_arg in args[4:]:
2659 if cur_arg == 'writable':
2660 writable = True
2661 if cur_arg == 'persistent':
2662 persistent = True
2663 if persistent:
2664 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.createSharedFolder(name, path, writable), [])
2665 else:
2666 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: console.createSharedFolder(name, path, writable)])
2667 return 0
2668
2669def unshareFolderCmd(ctx, args):
2670 if len(args) < 3:
2671 print("usage: unshareFolder <vmname|uuid> <name>")
2672 return 0
2673
2674 mach = argsToMach(ctx, args)
2675 if mach is None:
2676 return 0
2677 name = args[2]
2678 found = False
2679 for sharedfolder in ctx['global'].getArray(mach, 'sharedFolders'):
2680 if sharedfolder.name == name:
2681 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.removeSharedFolder(name), [])
2682 found = True
2683 break
2684 if not found:
2685 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: console.removeSharedFolder(name)])
2686 return 0
2687
2688
2689def snapshotCmd(ctx, args):
2690 if (len(args) < 2 or args[1] == 'help'):
2691 print("Take snapshot: snapshot <vmname|uuid> take <name> <description>")
2692 print("Restore snapshot: snapshot <vmname|uuid> restore <name>")
2693 print("Merge snapshot: snapshot <vmname|uuid> merge <name>")
2694 return 0
2695
2696 mach = argsToMach(ctx, args)
2697 if mach is None:
2698 return 0
2699 cmd = args[2]
2700 if cmd == 'take':
2701 if len(args) < 4:
2702 print("usage: snapshot <vmname|uuid> take <name> <description>")
2703 return 0
2704 name = args[3]
2705 if len(args) > 4:
2706 desc = args[4]
2707 else:
2708 desc = ""
2709 cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.takeSnapshot(name, desc, True)[0]))
2710 return 0
2711
2712 if cmd == 'restore':
2713 if len(args) < 4:
2714 print("usage: snapshot <vmname|uuid> restore <name>")
2715 return 0
2716 name = args[3]
2717 snap = mach.findSnapshot(name)
2718 cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.restoreSnapshot(snap)))
2719 return 0
2720
2721 if cmd == 'restorecurrent':
2722 if len(args) < 4:
2723 print("usage: snapshot <vmname|uuid> restorecurrent")
2724 return 0
2725 snap = mach.currentSnapshot()
2726 cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.restoreSnapshot(snap)))
2727 return 0
2728
2729 if cmd == 'delete':
2730 if len(args) < 4:
2731 print("usage: snapshot <vmname|uuid> delete <name>")
2732 return 0
2733 name = args[3]
2734 snap = mach.findSnapshot(name)
2735 cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.deleteSnapshot(snap.id)))
2736 return 0
2737
2738 print("Command '%s' is unknown" % (cmd))
2739 return 0
2740
2741def natAlias(_ctx, _mach, _nicnum, nat, args=None):
2742 """This command shows/alters NAT's alias settings.
2743 usage: nat <vmname|uuid> <nicnum> alias [default|[log] [proxyonly] [sameports]]
2744 default - set settings to default values
2745 log - switch on alias logging
2746 proxyonly - switch proxyonly mode on
2747 sameports - enforces NAT using the same ports
2748 """
2749 alias = {
2750 'log': 0x1,
2751 'proxyonly': 0x2,
2752 'sameports': 0x4
2753 }
2754 if len(args) == 1:
2755 first = 0
2756 msg = ''
2757 for aliasmode, aliaskey in list(alias.items()):
2758 if first == 0:
2759 first = 1
2760 else:
2761 msg += ', '
2762 if int(nat.aliasMode) & aliaskey:
2763 msg += '%s: %s' % (aliasmode, 'on')
2764 else:
2765 msg += '%s: %s' % (aliasmode, 'off')
2766 return (0, [msg])
2767
2768 nat.aliasMode = 0
2769 if 'default' not in args:
2770 for idx in range(1, len(args)):
2771 if args[idx] not in alias:
2772 print('Invalid alias mode: ' + args[idx])
2773 print(natAlias.__doc__)
2774 return (1, None)
2775 nat.aliasMode = int(nat.aliasMode) | alias[args[idx]]
2776 return (0, None)
2777
2778def natSettings(_ctx, _mach, _nicnum, nat, args):
2779 """
2780 This command shows/alters NAT settings.
2781 usage: nat <vmname|uuid> <nicnum> settings [<mtu> [[<socsndbuf> <sockrcvbuf> [<tcpsndwnd> <tcprcvwnd>]]]]
2782 mtu - set mtu <= 16000
2783 socksndbuf/sockrcvbuf - sets amount of kb for socket sending/receiving buffer
2784 tcpsndwnd/tcprcvwnd - sets size of initial tcp sending/receiving window
2785 """
2786 if len(args) == 1:
2787 (mtu, socksndbuf, sockrcvbuf, tcpsndwnd, tcprcvwnd) = nat.getNetworkSettings()
2788 if mtu == 0: mtu = 1500
2789 if socksndbuf == 0: socksndbuf = 64
2790 if sockrcvbuf == 0: sockrcvbuf = 64
2791 if tcpsndwnd == 0: tcpsndwnd = 64
2792 if tcprcvwnd == 0: tcprcvwnd = 64
2793 msg = 'mtu:%s socket(snd:%s, rcv:%s) tcpwnd(snd:%s, rcv:%s)' % (mtu, socksndbuf, sockrcvbuf, tcpsndwnd, tcprcvwnd)
2794 return (0, [msg])
2795
2796 if args[1] < 16000:
2797 print('invalid mtu value (%s not in range [65 - 16000])' % (args[1]))
2798 return (1, None)
2799 for i in range(2, len(args)):
2800 if not args[i].isdigit() or int(args[i]) < 8 or int(args[i]) > 1024:
2801 print('invalid %s parameter (%i not in range [8-1024])' % (i, args[i]))
2802 return (1, None)
2803 nic_args = [args[1]]
2804 if len(args) < 6:
2805 for i in range(2, len(args)): nic_args.append(args[i])
2806 for i in range(len(args), 6): nic_args.append(0)
2807 else:
2808 for i in range(2, len(args)): nic_args.append(args[i])
2809 #print(a)
2810 nat.setNetworkSettings(int(nic_args[0]), int(nic_args[1]), int(nic_args[2]), int(nic_args[3]), int(nic_args[4]))
2811 return (0, None)
2812
2813def natDns(_ctx, _mach, _nicnum, nat, args):
2814 """This command shows/alters DNS's NAT settings
2815 usage: nat <vmname|uuid> <nicnum> dns [passdomain] [proxy] [usehostresolver]
2816 passdomain - enforces builtin DHCP server to pass domain
2817 proxy - switch on builtin NAT DNS proxying mechanism
2818 usehostresolver - proxies all DNS requests to Host Resolver interface
2819 """
2820 yesno = {0: 'off', 1: 'on'}
2821 if len(args) == 1:
2822 msg = 'passdomain:%s, proxy:%s, usehostresolver:%s' % (yesno[int(nat.DNSPassDomain)], yesno[int(nat.DNSProxy)], yesno[int(nat.DNSUseHostResolver)])
2823 return (0, [msg])
2824
2825 nat.DNSPassDomain = 'passdomain' in args
2826 nat.DNSProxy = 'proxy' in args
2827 nat.DNSUseHostResolver = 'usehostresolver' in args
2828 return (0, None)
2829
2830def natTftp(ctx, mach, nicnum, nat, args):
2831 """This command shows/alters TFTP settings
2832 usage nat <vmname|uuid> <nicnum> tftp [prefix <prefix>| bootfile <bootfile>| server <server>]
2833 prefix - alters prefix TFTP settings
2834 bootfile - alters bootfile TFTP settings
2835 server - sets booting server
2836 """
2837 if len(args) == 1:
2838 server = nat.TFTPNextServer
2839 if server is None:
2840 server = nat.network
2841 if server is None:
2842 server = '10.0.%d/24' % (int(nicnum) + 2)
2843 (server, _mask) = server.split('/')
2844 while server.count('.') != 3:
2845 server += '.0'
2846 (ipA, ipB, ipC, _ipD) = server.split('.')
2847 server = '%d.%d.%d.4' % (ipA, ipB, ipC)
2848 prefix = nat.TFTPPrefix
2849 if prefix is None:
2850 prefix = '%s/TFTP/' % (ctx['vb'].homeFolder)
2851 bootfile = nat.TFTPBootFile
2852 if bootfile is None:
2853 bootfile = '%s.pxe' % (mach.name)
2854 msg = 'server:%s, prefix:%s, bootfile:%s' % (server, prefix, bootfile)
2855 return (0, [msg])
2856
2857 cmd = args[1]
2858 if len(args) != 3:
2859 print('invalid args:', args)
2860 print(natTftp.__doc__)
2861 return (1, None)
2862 if cmd == 'prefix': nat.TFTPPrefix = args[2]
2863 elif cmd == 'bootfile': nat.TFTPBootFile = args[2]
2864 elif cmd == 'server': nat.TFTPNextServer = args[2]
2865 else:
2866 print("invalid cmd:", cmd)
2867 return (1, None)
2868 return (0, None)
2869
2870def natPortForwarding(ctx, _mach, _nicnum, nat, args):
2871 """This command shows/manages port-forwarding settings
2872 usage:
2873 nat <vmname|uuid> <nicnum> <pf> [ simple tcp|udp <hostport> <guestport>]
2874 |[no_name tcp|udp <hostip> <hostport> <guestip> <guestport>]
2875 |[ex tcp|udp <pf-name> <hostip> <hostport> <guestip> <guestport>]
2876 |[delete <pf-name>]
2877 """
2878 if len(args) == 1:
2879 # note: keys/values are swapped in defining part of the function
2880 proto = {0: 'udp', 1: 'tcp'}
2881 msg = []
2882 port_forwardings = ctx['global'].getArray(nat, 'redirects')
2883 for forwarding in port_forwardings:
2884 (pfnme, pfp, pfhip, pfhp, pfgip, pfgp) = str(forwarding).split(', ')
2885 msg.append('%s: %s %s:%s => %s:%s' % (pfnme, proto[int(pfp)], pfhip, pfhp, pfgip, pfgp))
2886 return (0, msg) # msg is array
2887
2888 proto = {'udp': 0, 'tcp': 1}
2889 pfcmd = {
2890 'simple': {
2891 'validate': lambda: args[1] in list(pfcmd) and args[2] in list(proto) and len(args) == 5,
2892 'func':lambda: nat.addRedirect('', proto[args[2]], '', int(args[3]), '', int(args[4]))
2893 },
2894 'no_name': {
2895 'validate': lambda: args[1] in list(pfcmd) and args[2] in list(proto) and len(args) == 7,
2896 'func': lambda: nat.addRedirect('', proto[args[2]], args[3], int(args[4]), args[5], int(args[6]))
2897 },
2898 'ex': {
2899 'validate': lambda: args[1] in list(pfcmd) and args[2] in list(proto) and len(args) == 8,
2900 'func': lambda: nat.addRedirect(args[3], proto[args[2]], args[4], int(args[5]), args[6], int(args[7]))
2901 },
2902 'delete': {
2903 'validate': lambda: len(args) == 3,
2904 'func': lambda: nat.removeRedirect(args[2])
2905 }
2906 }
2907
2908 if not pfcmd[args[1]]['validate']():
2909 print('invalid port-forwarding or args of sub command ', args[1])
2910 print(natPortForwarding.__doc__)
2911 return (1, None)
2912
2913 _not_sure_for_what_this_is = pfcmd[args[1]]['func']()
2914 return (0, None)
2915
2916def natNetwork(_ctx, _mach, nicnum, nat, args):
2917 """This command shows/alters NAT network settings
2918 usage: nat <vmname|uuid> <nicnum> network [<network>]
2919 """
2920 if len(args) == 1:
2921 if nat.network is not None and len(str(nat.network)) != 0:
2922 msg = '\'%s\'' % (nat.network)
2923 else:
2924 msg = '10.0.%d.0/24' % (int(nicnum) + 2)
2925 return (0, [msg])
2926
2927 (addr, mask) = args[1].split('/')
2928 if addr.count('.') > 3 or int(mask) < 0 or int(mask) > 32:
2929 print('Invalid arguments')
2930 return (1, None)
2931 nat.network = args[1]
2932 return (0, None)
2933
2934def natCmd(ctx, args):
2935 """This command is entry point to NAT settins management
2936 usage: nat <vmname|uuid> <nicnum> <cmd> <cmd-args>
2937 cmd - [alias|settings|tftp|dns|pf|network]
2938 for more information about commands:
2939 nat help <cmd>
2940 """
2941
2942 natcommands = {
2943 'alias' : natAlias,
2944 'settings' : natSettings,
2945 'tftp': natTftp,
2946 'dns': natDns,
2947 'pf': natPortForwarding,
2948 'network': natNetwork
2949 }
2950
2951 if len(args) < 2 or args[1] == 'help':
2952 if len(args) > 2:
2953 print(natcommands[args[2]].__doc__)
2954 else:
2955 print(natCmd.__doc__)
2956 return 0
2957 if len(args) == 1 or len(args) < 4 or args[3] not in natcommands:
2958 print(natCmd.__doc__)
2959 return 0
2960 mach = argsToMach(ctx, args)
2961 if not mach:
2962 print("please specify vm")
2963 return 0
2964 platformProps = mach.platform.properties
2965 if len(args) < 3 or not args[2].isdigit() or int(args[2]) not in list(range(0, platformProps.getMaxNetworkAdapters(mach.platform.chipsetType))):
2966 print('please specify adapter num %d isn\'t in range [0-%d]' % (args[2], platformProps.getMaxNetworkAdapters(mach.platform.chipsetType)))
2967 return 0
2968 nicnum = int(args[2])
2969 cmdargs = []
2970 for i in range(3, len(args)):
2971 cmdargs.append(args[i])
2972
2973 # @todo vvl if nicnum is missed but command is entered
2974 # use NAT func for every adapter on machine.
2975 func = args[3]
2976 rosession = 1
2977 session = None
2978 if len(cmdargs) > 1:
2979 rosession = 0
2980 session = ctx['global'].openMachineSession(mach, fPermitSharing=False)
2981 mach = session.machine
2982
2983 adapter = mach.getNetworkAdapter(nicnum)
2984 natEngine = adapter.NATEngine
2985 (rc, reports) = natcommands[func](ctx, mach, nicnum, natEngine, cmdargs)
2986 if rosession == 0:
2987 if rc == 0:
2988 mach.saveSettings()
2989 session.unlockMachine()
2990 elif reports:
2991 for cur_report in reports:
2992 msg ='%s nic%d %s: %s' % (mach.name, nicnum, func, cur_report)
2993 print(msg)
2994 return 0
2995
2996def nicSwitchOnOff(adapter, attr, args):
2997 if len(args) == 1:
2998 yesno = {0: 'off', 1: 'on'}
2999 resp = yesno[int(adapter.getattr(attr))]
3000 return (0, resp)
3001
3002 yesno = {'off' : 0, 'on' : 1}
3003 if args[1] not in yesno:
3004 print('%s isn\'t acceptable, please choose %s' % (args[1], list(yesno.keys())))
3005 return (1, None)
3006 adapter.setsetattr(attr, yesno[args[1]])
3007 return (0, None)
3008
3009def nicTraceSubCmd(_ctx, _vm, _nicnum, adapter, args):
3010 '''
3011 usage: nic <vmname|uuid> <nicnum> trace [on|off [file]]
3012 '''
3013 (rc, resp) = nicSwitchOnOff(adapter, 'traceEnabled', args)
3014 if len(args) == 1 and rc == 0:
3015 resp = '%s file:%s' % (resp, adapter.traceFile)
3016 return (0, resp)
3017 if len(args) == 3 and rc == 0:
3018 adapter.traceFile = args[2]
3019 return (0, None)
3020
3021def nicLineSpeedSubCmd(_ctx, _vm, _nicnum, adapter, args):
3022 if len(args) == 1:
3023 resp = '%d kbps'% (adapter.lineSpeed)
3024 return (0, resp)
3025
3026 if not args[1].isdigit():
3027 print('%s isn\'t a number' % (args[1]))
3028 return (1, None)
3029 adapter.lineSpeed = int(args[1])
3030 return (0, None)
3031
3032def nicCableSubCmd(_ctx, _vm, _nicnum, adapter, args):
3033 '''
3034 usage: nic <vmname|uuid> <nicnum> cable [on|off]
3035 '''
3036 return nicSwitchOnOff(adapter, 'cableConnected', args)
3037
3038def nicEnableSubCmd(_ctx, _vm, _nicnum, adapter, args):
3039 '''
3040 usage: nic <vmname|uuid> <nicnum> enable [on|off]
3041 '''
3042 return nicSwitchOnOff(adapter, 'enabled', args)
3043
3044def nicTypeSubCmd(ctx, _vm, _nicnum, adapter, args):
3045 '''
3046 usage: nic <vmname|uuid> <nicnum> type [Am79c970A|Am79c970A|I82540EM|I82545EM|I82543GC|Virtio]
3047 '''
3048 if len(args) == 1:
3049 nictypes = ctx['const'].all_values('NetworkAdapterType')
3050 for key in list(nictypes.keys()):
3051 if str(adapter.adapterType) == str(nictypes[key]):
3052 return (0, str(key))
3053 return (1, None)
3054
3055 nictypes = ctx['const'].all_values('NetworkAdapterType')
3056 if args[1] not in list(nictypes.keys()):
3057 print('%s not in acceptable values (%s)' % (args[1], list(nictypes.keys())))
3058 return (1, None)
3059 adapter.adapterType = nictypes[args[1]]
3060 return (0, None)
3061
3062def nicAttachmentSubCmd(ctx, _vm, _nicnum, adapter, args):
3063 '''
3064 usage: nic <vmname|uuid> <nicnum> attachment [Null|NAT|Bridged <interface>|Internal <name>|HostOnly <interface>
3065 '''
3066 if len(args) == 1:
3067 nicAttachmentType = {
3068 ctx['global'].constants.NetworkAttachmentType_Null: ('Null', ''),
3069 ctx['global'].constants.NetworkAttachmentType_NAT: ('NAT', ''),
3070 ctx['global'].constants.NetworkAttachmentType_Bridged: ('Bridged', adapter.bridgedInterface),
3071 ctx['global'].constants.NetworkAttachmentType_Internal: ('Internal', adapter.internalNetwork),
3072 ctx['global'].constants.NetworkAttachmentType_HostOnly: ('HostOnly', adapter.hostOnlyInterface),
3073 # @todo show details of the generic network attachment type
3074 ctx['global'].constants.NetworkAttachmentType_Generic: ('Generic', ''),
3075 }
3076 if not isinstance(adapter.attachmentType, int):
3077 t = str(adapter.attachmentType)
3078 else:
3079 t = adapter.attachmentType
3080 (resp, name) = nicAttachmentType[t]
3081 return (0, 'attachment:%s, name:%s' % (resp, name))
3082
3083 nicAttachmentType = {
3084 'Null': {
3085 'v': lambda: len(args) == 2,
3086 'p': lambda: 'do nothing',
3087 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Null},
3088 'NAT': {
3089 'v': lambda: len(args) == 2,
3090 'p': lambda: 'do nothing',
3091 'f': lambda: ctx['global'].constants.NetworkAttachmentType_NAT},
3092 'Bridged': {
3093 'v': lambda: len(args) == 3,
3094 'p': lambda: adapter.setattr('bridgedInterface', args[2]),
3095 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Bridged},
3096 'Internal': {
3097 'v': lambda: len(args) == 3,
3098 'p': lambda: adapter.setattr('internalNetwork', args[2]),
3099 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Internal},
3100 'HostOnly': {
3101 'v': lambda: len(args) == 2,
3102 'p': lambda: adapter.setattr('hostOnlyInterface', args[2]),
3103 'f': lambda: ctx['global'].constants.NetworkAttachmentType_HostOnly},
3104 # @todo implement setting the properties of a generic attachment
3105 'Generic': {
3106 'v': lambda: len(args) == 3,
3107 'p': lambda: 'do nothing',
3108 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Generic}
3109 }
3110 if args[1] not in list(nicAttachmentType):
3111 print('%s not in acceptable values (%s)' % (args[1], list(nicAttachmentType.keys())))
3112 return (1, None)
3113 if not nicAttachmentType[args[1]]['v']():
3114 ## @todo r=andy Log this properly!
3115 return (1, None)
3116 nicAttachmentType[args[1]]['p']()
3117 adapter.attachmentType = nicAttachmentType[args[1]]['f']()
3118 return (0, None)
3119
3120def nicCmd(ctx, args):
3121 '''
3122 This command to manage network adapters
3123 usage: nic <vmname|uuid> <nicnum> <cmd> <cmd-args>
3124 where cmd : attachment, trace, linespeed, cable, enable, type
3125 '''
3126 # 'command name':{'runtime': is_callable_at_runtime, 'op': function_name}
3127 niccomand = {
3128 'attachment': nicAttachmentSubCmd,
3129 'trace': nicTraceSubCmd,
3130 'linespeed': nicLineSpeedSubCmd,
3131 'cable': nicCableSubCmd,
3132 'enable': nicEnableSubCmd,
3133 'type': nicTypeSubCmd
3134 }
3135 if len(args) < 2 \
3136 or args[1] == 'help' \
3137 or (len(args) > 2 and args[3] not in niccomand):
3138 if len(args) == 3 \
3139 and args[2] in niccomand:
3140 print(niccomand[args[2]].__doc__)
3141 else:
3142 print(nicCmd.__doc__)
3143 return 0
3144
3145 mach = ctx['argsToMach'](args)
3146 if not mach:
3147 return 1
3148
3149 platformProps = mach.platform.properties
3150 if len(args) < 3 \
3151 or int(args[2]) not in list(range(0, platformProps.getMaxNetworkAdapters(mach.platform.chipsetType))):
3152 print('please specify adapter num %d isn\'t in range [0-%d]'% (args[2], platformProps.getMaxNetworkAdapters(mach.platform.chipsetType)))
3153 return 1
3154 nicnum = int(args[2])
3155 cmdargs = args[3:]
3156 func = args[3]
3157 session = None
3158 session = ctx['global'].openMachineSession(mach, fPermitSharing=True)
3159 mach = session.machine
3160 adapter = mach.getNetworkAdapter(nicnum)
3161 (rc, report) = niccomand[func](ctx, mach, nicnum, adapter, cmdargs)
3162 if rc == 0:
3163 mach.saveSettings()
3164 if report is not None:
3165 print('%s nic %d %s: %s' % (mach.name, nicnum, args[3], report))
3166 session.unlockMachine()
3167 return 0
3168
3169
3170def promptCmd(ctx, args):
3171 if len(args) < 2:
3172 print("Current prompt: '%s'" % (ctx['prompt']))
3173 return 0
3174
3175 ctx['prompt'] = args[1]
3176 return 0
3177
3178def foreachCmd(ctx, args):
3179 if len(args) < 3:
3180 print("usage: foreach scope command, where scope is XPath-like expression //vms/vm[@CPUCount='2']")
3181 return 0
3182
3183 scope = args[1]
3184 cmd = args[2]
3185 elems = eval_xpath(ctx, scope)
3186 try:
3187 for e in elems:
3188 e.apply(cmd)
3189 except:
3190 print("Error executing")
3191 traceback.print_exc()
3192 return 0
3193
3194def foreachvmCmd(ctx, args):
3195 if len(args) < 2:
3196 print("foreachvm command <args>")
3197 return 0
3198 cmdargs = args[1:]
3199 cmdargs.insert(1, '')
3200 for mach in getMachines(ctx):
3201 cmdargs[1] = mach.id
3202 runCommandArgs(ctx, cmdargs)
3203 return 0
3204
3205def recordDemoCmd(ctx, args):
3206 if len(args) < 3:
3207 print("usage: recordDemo <vmname|uuid> <filename> [duration in s]")
3208 return 0
3209 mach = argsToMach(ctx, args)
3210 if not mach:
3211 return 0
3212 filename = args[2]
3213 dur = 10000
3214 if len(args) > 3:
3215 dur = float(args[3])
3216 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: recordDemo(ctx, console, filename, dur)])
3217 return 0
3218
3219def playbackDemoCmd(ctx, args):
3220 if len(args) < 3:
3221 print("usage: playbackDemo <vmname|uuid> <filename> [duration in s]")
3222 return 0
3223 mach = argsToMach(ctx, args)
3224 if not mach:
3225 return 0
3226 filename = args[2]
3227 dur = 10000
3228 if len(args) > 3:
3229 dur = float(args[3])
3230 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: playbackDemo(ctx, console, filename, dur)])
3231 return 0
3232
3233
3234def pciAddr(ctx, addr):
3235 strg = "%02x:%02x.%d" % (addr >> 8, (addr & 0xff) >> 3, addr & 7)
3236 return colPci(ctx, strg)
3237
3238def lspci(ctx, console):
3239 assigned = ctx['global'].getArray(console.machine, 'PCIDeviceAssignments')
3240 for assignment in assigned:
3241 if assignment.isPhysicalDevice:
3242 print("%s: assigned host device %s guest %s" % (colDev(ctx, assignment.name), pciAddr(ctx, assignment.hostAddress), pciAddr(ctx, assignment.guestAddress)))
3243
3244 atts = ctx['global'].getArray(console, 'attachedPCIDevices')
3245 for att in atts:
3246 if att.isPhysicalDevice:
3247 print("%s: physical, guest %s, host %s" % (colDev(ctx, att.name), pciAddr(ctx, att.guestAddress), pciAddr(ctx, att.hostAddress)))
3248 else:
3249 print("%s: virtual, guest %s" % (colDev(ctx, att.name), pciAddr(ctx, att.guestAddress)))
3250 return
3251
3252def parsePci(strg):
3253 pcire = re.compile(r'(?P<b>[0-9a-fA-F]+):(?P<d>[0-9a-fA-F]+)\.(?P<f>\d)')
3254 match = pcire.search(strg)
3255 if match is None:
3256 return -1
3257 pdict = match.groupdict()
3258 return ((int(pdict['b'], 16)) << 8) | ((int(pdict['d'], 16)) << 3) | int(pdict['f'])
3259
3260def lspciCmd(ctx, args):
3261 if len(args) < 2:
3262 print("usage: lspci vm")
3263 return 0
3264 mach = argsToMach(ctx, args)
3265 if not mach:
3266 return 0
3267 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: lspci(ctx, console)])
3268 return 0
3269
3270def attachpciCmd(ctx, args):
3271 if len(args) < 3:
3272 print("usage: attachpci <vmname|uuid> <host pci address> <guest pci address>")
3273 return 0
3274 mach = argsToMach(ctx, args)
3275 if not mach:
3276 return 0
3277 hostaddr = parsePci(args[2])
3278 if hostaddr == -1:
3279 print("invalid host PCI %s, accepted format 01:02.3 for bus 1, device 2, function 3" % (args[2]))
3280 return 0
3281
3282 if len(args) > 3:
3283 guestaddr = parsePci(args[3])
3284 if guestaddr == -1:
3285 print("invalid guest PCI %s, accepted format 01:02.3 for bus 1, device 2, function 3" % (args[3]))
3286 return 0
3287 else:
3288 guestaddr = hostaddr
3289 cmdClosedVm(ctx, mach, lambda ctx, mach, a: mach.attachHostPCIDevice(hostaddr, guestaddr, True))
3290 return 0
3291
3292def detachpciCmd(ctx, args):
3293 if len(args) < 3:
3294 print("usage: detachpci <vmname|uuid> <host pci address>")
3295 return 0
3296 mach = argsToMach(ctx, args)
3297 if not mach:
3298 return 0
3299 hostaddr = parsePci(args[2])
3300 if hostaddr == -1:
3301 print("invalid host PCI %s, accepted format 01:02.3 for bus 1, device 2, function 3" % (args[2]))
3302 return 0
3303
3304 cmdClosedVm(ctx, mach, lambda ctx, mach, a: mach.detachHostPCIDevice(hostaddr))
3305 return 0
3306
3307def gotoCmd(ctx, args):
3308 if len(args) < 2:
3309 print("usage: goto line")
3310 return 0
3311
3312 line = int(args[1])
3313
3314 ctx['scriptLine'] = line
3315
3316 return 0
3317
3318aliases = {'s':'start',
3319 'i':'info',
3320 'l':'list',
3321 'h':'help',
3322 'a':'alias',
3323 'q':'quit', 'exit':'quit',
3324 'tg': 'typeGuest',
3325 'v':'verbose'}
3326
3327commands = {'help':['Prints help information', helpCmd, 0],
3328 'start':['Start virtual machine by name or uuid: start mytestvm headless', startCmd, 0],
3329 'createVm':['Create virtual machine: createVm myvmname x86 MacOS', createVmCmd, 0],
3330 'removeVm':['Remove virtual machine', removeVmCmd, 0],
3331 'pause':['Pause virtual machine', pauseCmd, 0],
3332 'resume':['Resume virtual machine', resumeCmd, 0],
3333 'save':['Save execution state of virtual machine', saveCmd, 0],
3334 'stats':['Stats for virtual machine', statsCmd, 0],
3335 'powerdown':['Power down virtual machine', powerdownCmd, 0],
3336 'powerbutton':['Effectively press power button', powerbuttonCmd, 0],
3337 'list':['Shows known virtual machines', listCmd, 0],
3338 'info':['Shows info on machine', infoCmd, 0],
3339 'ginfo':['Shows info on guest', ginfoCmd, 0],
3340 'gexec':['Executes program in the guest', gexecCmd, 0],
3341 'gcopy':['Copy file to the guest', gcopyCmd, 0],
3342 'gpipe':['Pipe between host and guest', gpipeCmd, 0],
3343 'alias':['Control aliases', aliasCmd, 0],
3344 'verbose':['Toggle verbosity', verboseCmd, 0],
3345 'setvar':['Set VM variable: setvar mytestvm firmwareSettings.ACPIEnabled True', setvarCmd, 0],
3346 'eval':['Evaluate arbitrary Python construction: eval \'for m in getMachines(ctx): print(m.name, "has", m.memorySize, "M")\'', evalCmd, 0],
3347 'quit':['Exits', quitCmd, 0],
3348 'host':['Show host information', hostCmd, 0],
3349 'guest':['Execute command for guest: guest mytestvm \'console.mouse.putMouseEvent(20, 20, 0, 0, 0)\'', guestCmd, 0],
3350 'monitorGuest':['Monitor what happens with the guest for some time: monitorGuest mytestvm 10', monitorGuestCmd, 0],
3351 'monitorGuestKbd':['Monitor guest keyboard for some time: monitorGuestKbd mytestvm 10', monitorGuestKbdCmd, 0],
3352 'monitorGuestMouse':['Monitor guest mouse for some time: monitorGuestMouse mytestvm 10', monitorGuestMouseCmd, 0],
3353 'monitorGuestMultiTouch':['Monitor guest touch screen for some time: monitorGuestMultiTouch mytestvm 10', monitorGuestMultiTouchCmd, 0],
3354 'monitorVBox':['Monitor what happens with VirtualBox for some time: monitorVBox 10', monitorVBoxCmd, 0],
3355 'portForward':['Setup permanent port forwarding for a VM, takes adapter number host port and guest port: portForward mytestvm 0 8080 80', portForwardCmd, 0],
3356 'showLog':['Show log file of the VM, : showLog mytestvm', showLogCmd, 0],
3357 'findLog':['Show entries matching pattern in log file of the VM, : findLog mytestvm PDM|CPUM', findLogCmd, 0],
3358 'findAssert':['Find assert in log file of the VM, : findAssert mytestvm', findAssertCmd, 0],
3359 'reloadExt':['Reload custom extensions: reloadExt', reloadExtCmd, 0],
3360 'runScript':['Run VBox script: runScript script.vbox', runScriptCmd, 0],
3361 'sleep':['Sleep for specified number of seconds: sleep 3.14159', sleepCmd, 0],
3362 'shell':['Execute external shell command: shell "ls /etc/rc*"', shellCmd, 0],
3363 'exportVm':['Export VM in OVF format: exportVm mytestvm /tmp/win.ovf', exportVMCmd, 0],
3364 'screenshot':['Take VM screenshot to a file: screenshot mytestvm /tmp/win.png 1024 768 0', screenshotCmd, 0],
3365 'teleport':['Teleport VM to another box (see openportal): teleport mytestvm anotherhost:8000 <passwd> <maxDowntime>', teleportCmd, 0],
3366 'typeGuest':['Type arbitrary text in guest: typeGuest Linux "^lls\\n&UP;&BKSP;ess /etc/hosts\\nq^c" 0.7', typeGuestCmd, 0],
3367 'openportal':['Open portal for teleportation of VM from another box (see teleport): openportal mytestvm 8000 <passwd>', openportalCmd, 0],
3368 'closeportal':['Close teleportation portal (see openportal, teleport): closeportal Win', closeportalCmd, 0],
3369 'getextra':['Get extra data, empty key lists all: getextra <vm|global> <key>', getExtraDataCmd, 0],
3370 'setextra':['Set extra data, empty value removes key: setextra <vm|global> <key> <value>', setExtraDataCmd, 0],
3371 'gueststats':['Print available guest stats (only Windows guests with additions so far): gueststats mytestvm', gueststatsCmd, 0],
3372 'plugcpu':['Add a CPU to a running VM: plugcpu mytestvm 1', plugcpuCmd, 0],
3373 'unplugcpu':['Remove a CPU from a running VM (additions required, Windows cannot unplug): unplugcpu Linux 1', unplugcpuCmd, 0],
3374 'createHdd': ['Create virtual HDD: createHdd 1000 /disk.vdi ', createHddCmd, 0],
3375 'removeHdd': ['Permanently remove virtual HDD: removeHdd /disk.vdi', removeHddCmd, 0],
3376 'registerHdd': ['Register HDD image with VirtualBox instance: registerHdd /disk.vdi', registerHddCmd, 0],
3377 'unregisterHdd': ['Unregister HDD image with VirtualBox instance: unregisterHdd /disk.vdi', unregisterHddCmd, 0],
3378 'attachHdd': ['Attach HDD to the VM: attachHdd mytestvm /disk.vdi "IDE Controller" 0:1', attachHddCmd, 0],
3379 'detachHdd': ['Detach HDD from the VM: detachHdd mytestvm /disk.vdi', detachHddCmd, 0],
3380 'registerIso': ['Register CD/DVD image with VirtualBox instance: registerIso /os.iso', registerIsoCmd, 0],
3381 'unregisterIso': ['Unregister CD/DVD image with VirtualBox instance: unregisterIso /os.iso', unregisterIsoCmd, 0],
3382 'removeIso': ['Permanently remove CD/DVD image: removeIso /os.iso', removeIsoCmd, 0],
3383 'attachIso': ['Attach CD/DVD to the VM: attachIso mytestvm /os.iso "IDE Controller" 0:1', attachIsoCmd, 0],
3384 'detachIso': ['Detach CD/DVD from the VM: detachIso mytestvm /os.iso', detachIsoCmd, 0],
3385 'mountIso': ['Mount CD/DVD to the running VM: mountIso mytestvm /os.iso "IDE Controller" 0:1', mountIsoCmd, 0],
3386 'unmountIso': ['Unmount CD/DVD from running VM: unmountIso mytestvm "IDE Controller" 0:1', unmountIsoCmd, 0],
3387 'attachCtr': ['Attach storage controller to the VM: attachCtr mytestvm Ctr0 IDE ICH6', attachCtrCmd, 0],
3388 'detachCtr': ['Detach HDD from the VM: detachCtr mytestvm Ctr0', detachCtrCmd, 0],
3389 'attachUsb': ['Attach USB device to the VM (use listUsb to show available devices): attachUsb mytestvm uuid', attachUsbCmd, 0],
3390 'detachUsb': ['Detach USB device from the VM: detachUsb mytestvm uuid', detachUsbCmd, 0],
3391 'listMedia': ['List media known to this VBox instance', listMediaCmd, 0],
3392 'listUsb': ['List known USB devices', listUsbCmd, 0],
3393 'shareFolder': ['Make host\'s folder visible to guest: shareFolder mytestvm /share share writable', shareFolderCmd, 0],
3394 'unshareFolder': ['Remove folder sharing', unshareFolderCmd, 0],
3395 'gui': ['Start GUI frontend', guiCmd, 0],
3396 'colors':['Toggle colors', colorsCmd, 0],
3397 'snapshot':['VM snapshot manipulation, snapshot help for more info', snapshotCmd, 0],
3398 'nat':['NAT (network address translation engine) manipulation, nat help for more info', natCmd, 0],
3399 'nic' : ['Network adapter management', nicCmd, 0],
3400 'prompt' : ['Control shell prompt', promptCmd, 0],
3401 'foreachvm' : ['Perform command for each VM', foreachvmCmd, 0],
3402 'foreach' : ['Generic "for each" construction, using XPath-like notation: foreach //vms/vm[@OSTypeId=\'MacOS\'] "print(obj.name)"', foreachCmd, 0],
3403 'recordDemo':['Record demo: recordDemo mytestvm file.dmo 10', recordDemoCmd, 0],
3404 'playbackDemo':['Playback demo: playbackDemo mytestvm file.dmo 10', playbackDemoCmd, 0],
3405 'lspci': ['List PCI devices attached to the VM: lspci mytestvm', lspciCmd, 0],
3406 'attachpci': ['Attach host PCI device to the VM: attachpci mytestvm 01:00.0', attachpciCmd, 0],
3407 'detachpci': ['Detach host PCI device from the VM: detachpci mytestvm 01:00.0', detachpciCmd, 0],
3408 'goto': ['Go to line in script (script-only)', gotoCmd, 0]
3409 }
3410
3411def runCommandArgs(ctx, args):
3412 c = args[0]
3413 if aliases.get(c, None):
3414 c = aliases[c]
3415 cmd_internal = commands.get(c, None)
3416 if not cmd_internal:
3417 print("Unknown command: '%s', type 'help' for list of known commands" % (c))
3418 return 0
3419 if ctx['remote'] and ctx['vb'] is None:
3420 if c not in ['connect', 'reconnect', 'help', 'quit']:
3421 print("First connect to remote server with %s command." % (colored('connect', 'blue')))
3422 return 0
3423 return cmd_internal[1](ctx, args)
3424
3425
3426def runCommand(ctx, cmd):
3427 if not cmd: return 0
3428 args = split_no_quotes(cmd)
3429 if len(args) == 0: return 0
3430 return runCommandArgs(ctx, args)
3431
3432#
3433# To write your own custom commands to vboxshell, create
3434# file ~/.VirtualBox/shellext.py with content like
3435#
3436# def runTestCmd(ctx, args):
3437# print("Testy test", ctx['vb'])
3438# return 0
3439#
3440# commands = {
3441# 'test': ['Test help', runTestCmd]
3442# }
3443# and issue reloadExt shell command.
3444# This file also will be read automatically on startup or 'reloadExt'.
3445#
3446# Also one can put shell extensions into ~/.VirtualBox/shexts and
3447# they will also be picked up, so this way one can exchange
3448# shell extensions easily.
3449def addExtsFromFile(_ctx, cmds, filename):
3450 if not os.path.isfile(filename):
3451 return
3452 extDict = {}
3453 try:
3454 with open(filename, encoding='utf-8') as file:
3455 file_buf = file.read()
3456 exec(compile(file_buf, filename, 'exec'), extDict, extDict) # pylint: disable=exec-used
3457 for (key, value) in list(extDict['commands'].items()):
3458 if g_fVerbose:
3459 print("customize: adding \"%s\" - %s" % (key, value[0]))
3460 cmds[key] = [value[0], value[1], filename]
3461 except:
3462 print("Error loading user extensions from %s" % (filename))
3463 traceback.print_exc()
3464
3465
3466def checkUserExtensions(ctx, cmds, folder):
3467 folder = str(folder)
3468 name = os.path.join(folder, "shellext.py")
3469 addExtsFromFile(ctx, cmds, name)
3470 # also check 'exts' directory for all files
3471 shextdir = os.path.join(folder, "shexts")
3472 if not os.path.isdir(shextdir):
3473 return
3474 exts = os.listdir(shextdir)
3475 for e in exts:
3476 # not editor temporary files, please.
3477 if e.endswith('.py'):
3478 addExtsFromFile(ctx, cmds, os.path.join(shextdir, e))
3479
3480def getHomeFolder(ctx):
3481 if ctx['remote'] or ctx['vb'] is None:
3482 if 'VBOX_USER_HOME' in os.environ:
3483 return os.path.join(os.environ['VBOX_USER_HOME'])
3484 return os.path.join(os.path.expanduser("~"), ".VirtualBox")
3485
3486 return ctx['vb'].homeFolder
3487
3488def interpret(ctx):
3489 if ctx['remote']:
3490 commands['connect'] = ["Connect to remote VBox instance: connect http://server:18083 user password", connectCmd, 0]
3491 commands['disconnect'] = ["Disconnect from remote VBox instance", disconnectCmd, 0]
3492 commands['reconnect'] = ["Reconnect to remote VBox instance", reconnectCmd, 0]
3493 ctx['wsinfo'] = ["http://localhost:18083", "", ""]
3494
3495 vbox = ctx['vb']
3496 if vbox is not None:
3497 try:
3498 print("Running VirtualBox version %s" % (vbox.version))
3499 except Exception as e:
3500 printErr(ctx, e)
3501 if g_fVerbose:
3502 traceback.print_exc()
3503 ctx['perf'] = None # ctx['global'].getPerfCollector(vbox)
3504 else:
3505 ctx['perf'] = None
3506
3507 home = getHomeFolder(ctx)
3508 checkUserExtensions(ctx, commands, home)
3509 if platform.system() in ['Windows', 'Microsoft']:
3510 global g_fHasColors
3511 g_fHasColors = False
3512 hist_file = os.path.join(home, ".vboxshellhistory")
3513 autoCompletion(commands, ctx)
3514
3515 if g_fHasReadline and os.path.exists(hist_file):
3516 readline.read_history_file(hist_file)
3517
3518 # to allow to print actual host information, we collect info for
3519 # last 150 secs maximum, (sample every 10 secs and keep up to 15 samples)
3520 if ctx['perf']:
3521 try:
3522 ctx['perf'].setup(['*'], [vbox.host], 10, 15)
3523 except:
3524 pass
3525 cmds = []
3526
3527 if g_sCmd is not None:
3528 cmds = g_sCmd.split(';')
3529 itCmd = iter(cmds)
3530
3531 while True:
3532 try:
3533 if g_fBatchMode:
3534 cmd = 'runScript %s'% (g_sScriptFile)
3535 elif g_sCmd is not None:
3536 cmd = next(itCmd)
3537 else:
3538 if sys.version_info[0] <= 2:
3539 cmd = raw_input(ctx['prompt']) # pylint: disable=undefined-variable
3540 else:
3541 cmd = input(ctx['prompt'])
3542 done = runCommand(ctx, cmd)
3543 if done != 0:
3544 break
3545 if g_fBatchMode:
3546 break
3547 except KeyboardInterrupt:
3548 print('====== You can type quit or q to leave')
3549 except StopIteration:
3550 break
3551 except EOFError:
3552 break
3553 except Exception as e:
3554 printErr(ctx, e)
3555 if g_fVerbose:
3556 traceback.print_exc()
3557 ctx['global'].waitForEvents(0)
3558 try:
3559 # There is no need to disable metric collection. This is just an example.
3560 if ctx['perf']:
3561 ctx['perf'].disable(['*'], [vbox.host])
3562 except:
3563 pass
3564 if g_fHasReadline:
3565 readline.write_history_file(hist_file)
3566
3567def runCommandCb(ctx, cmd, args):
3568 args.insert(0, cmd)
3569 return runCommandArgs(ctx, args)
3570
3571def runGuestCommandCb(ctx, uuid, guestLambda, args):
3572 mach = machById(ctx, uuid)
3573 if not mach:
3574 return 0
3575 args.insert(0, guestLambda)
3576 cmdExistingVm(ctx, mach, 'guestlambda', args)
3577 return 0
3578
3579def main(_argv):
3580
3581 #
3582 # Parse command line arguments.
3583 #
3584 parse = OptionParser()
3585 parse.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False, help = "switch on verbose")
3586 parse.add_option("-a", "--autopath", dest="autopath", action="store_true", default=False, help = "switch on autopath")
3587 parse.add_option("-w", "--webservice", dest="style", action="store_const", const="WEBSERVICE", help = "connect to webservice")
3588 parse.add_option("-b", "--batch", dest="batch_file", help = "script file to execute")
3589 parse.add_option("-c", dest="command_line", help = "command sequence to execute")
3590 parse.add_option("-o", dest="opt_line", help = "option line")
3591 global g_fVerbose, g_sScriptFile, g_fBatchMode, g_fHasColors, g_fHasReadline, g_sCmd
3592 (options, _args) = parse.parse_args()
3593 g_fVerbose = options.verbose
3594 style = options.style
3595 if options.batch_file is not None:
3596 g_fBatchMode = True
3597 g_fHasColors = False
3598 g_fHasReadline = False
3599 g_sScriptFile = options.batch_file
3600 if options.command_line is not None:
3601 g_fHasColors = False
3602 g_fHasReadline = False
3603 g_sCmd = options.command_line
3604
3605 params = None
3606 if options.opt_line is not None:
3607 params = {}
3608 strparams = options.opt_line
3609 strparamlist = strparams.split(',')
3610 for strparam in strparamlist:
3611 (key, value) = strparam.split('=')
3612 params[key] = value
3613
3614 if options.autopath:
3615 asLocations = [ os.getcwd(), ]
3616 try: sScriptDir = os.path.dirname(os.path.abspath(__file__))
3617 except: pass # In case __file__ isn't there.
3618 else:
3619 if platform.system() in [ 'SunOS', ]:
3620 asLocations.append(os.path.join(sScriptDir, 'amd64'))
3621 asLocations.append(sScriptDir)
3622
3623
3624 sPath = os.environ.get("VBOX_PROGRAM_PATH")
3625 if sPath is None:
3626 for sCurLoc in asLocations:
3627 if os.path.isfile(os.path.join(sCurLoc, "VirtualBox")) \
3628 or os.path.isfile(os.path.join(sCurLoc, "VirtualBox.exe")):
3629 print("Autodetected VBOX_PROGRAM_PATH as", sCurLoc)
3630 os.environ["VBOX_PROGRAM_PATH"] = sCurLoc
3631 sPath = sCurLoc
3632 break
3633 if sPath:
3634 sys.path.append(os.path.join(sPath, "sdk", "installer"))
3635
3636 sPath = os.environ.get("VBOX_SDK_PATH")
3637 if sPath is None:
3638 for sCurLoc in asLocations:
3639 if os.path.isfile(os.path.join(sCurLoc, "sdk", "bindings", "VirtualBox.xidl")):
3640 sCurLoc = os.path.join(sCurLoc, "sdk")
3641 print("Autodetected VBOX_SDK_PATH as", sCurLoc)
3642 os.environ["VBOX_SDK_PATH"] = sCurLoc
3643 sPath = sCurLoc
3644 break
3645 if sPath:
3646 sCurLoc = sPath
3647 sTmp = os.path.join(sCurLoc, 'bindings', 'xpcom', 'python')
3648 if os.path.isdir(sTmp):
3649 sys.path.append(sTmp)
3650 del sTmp
3651 del sPath, asLocations
3652
3653 #
3654 # Set up the shell interpreter context and start working.
3655 #
3656 from vboxapi import VirtualBoxManager
3657 oVBoxMgr = VirtualBoxManager(style, params)
3658 ctx = {
3659 'global': oVBoxMgr,
3660 'vb': oVBoxMgr.getVirtualBox(),
3661 'const': oVBoxMgr.constants,
3662 'remote': oVBoxMgr.remote,
3663 'type': oVBoxMgr.type,
3664 'run': lambda cmd, args: runCommandCb(ctx, cmd, args),
3665 'guestlambda': lambda uuid, guestLambda, args: runGuestCommandCb(ctx, uuid, guestLambda, args),
3666 'machById': lambda uuid: machById(ctx, uuid),
3667 'argsToMach': lambda args: argsToMach(ctx, args),
3668 'progressBar': lambda p: progressBar(ctx, p),
3669 'typeInGuest': typeInGuest,
3670 '_machlist': None,
3671 'prompt': g_sPrompt,
3672 'scriptLine': 0,
3673 'interrupt': False,
3674 }
3675 interpret(ctx)
3676
3677 #
3678 # Release the interfaces references in ctx before cleaning up.
3679 #
3680 for sKey in list(ctx.keys()):
3681 del ctx[sKey]
3682 ctx = None
3683 gc.collect()
3684
3685 oVBoxMgr.deinit()
3686 del oVBoxMgr
3687
3688if __name__ == '__main__':
3689 main(sys.argv)
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette