VirtualBox

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

Last change on this file since 102948 was 102948, checked in by vboxsync, 4 months ago

Main/Python bindings: Document + mark Python 2 as being deprecated.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 125.7 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 102948 2024-01-18 10:31:20Z 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: 102948 $"
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.accelerate3DEnabled))
1018 print(" Hardware 2d video acceleration [accelerate2DVideoEnabled]: " + asState(mach.graphicsAdapter.accelerate2DVideoEnabled))
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 if host.acceleration3DAvailable:
1636 print(colCat(ctx, "3D acceleration available"))
1637 else:
1638 print(colCat(ctx, "3D acceleration NOT available"))
1639
1640 print(colCat(ctx, "Network interfaces:"))
1641 for iface in ctx['global'].getArray(host, 'networkInterfaces'):
1642 print(" %s (%s)" % (iface.name, iface.IPAddress))
1643
1644 print(colCat(ctx, "DVD drives:"))
1645 for drive in ctx['global'].getArray(host, 'DVDDrives'):
1646 print(" %s - %s" % (drive.name, drive.description))
1647
1648 print(colCat(ctx, "Floppy drives:"))
1649 for drive in ctx['global'].getArray(host, 'floppyDrives'):
1650 print(" %s - %s" % (drive.name, drive.description))
1651
1652 print(colCat(ctx, "USB devices:"))
1653 for usbdev in ctx['global'].getArray(host, 'USBDevices'):
1654 printHostUsbDev(ctx, usbdev)
1655
1656 if ctx['perf']:
1657 for metric in ctx['perf'].query(["*"], [host]):
1658 print(metric['name'], metric['values_as_string'])
1659
1660 return 0
1661
1662def monitorGuestCmd(ctx, args):
1663 if len(args) < 2:
1664 print("usage: monitorGuest <vmname|uuid> (duration)")
1665 return 0
1666 mach = argsToMach(ctx, args)
1667 if not mach:
1668 return 0
1669 dur = 5
1670 if len(args) > 2:
1671 dur = float(args[2])
1672 active = False
1673 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.eventSource, active, dur)])
1674 return 0
1675
1676def monitorGuestKbdCmd(ctx, args):
1677 if len(args) < 2:
1678 print("usage: monitorGuestKbd name (duration)")
1679 return 0
1680 mach = argsToMach(ctx, args)
1681 if not mach:
1682 return 0
1683 dur = 5
1684 if len(args) > 2:
1685 dur = float(args[2])
1686 active = False
1687 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.keyboard.eventSource, active, dur)])
1688 return 0
1689
1690def monitorGuestMouseCmd(ctx, args):
1691 if len(args) < 2:
1692 print("usage: monitorGuestMouse name (duration)")
1693 return 0
1694 mach = argsToMach(ctx, args)
1695 if not mach:
1696 return 0
1697 dur = 5
1698 if len(args) > 2:
1699 dur = float(args[2])
1700 active = False
1701 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.mouse.eventSource, active, dur)])
1702 return 0
1703
1704def monitorGuestMultiTouchCmd(ctx, args):
1705 if len(args) < 2:
1706 print("usage: monitorGuestMultiTouch name (duration)")
1707 return 0
1708 mach = argsToMach(ctx, args)
1709 if not mach:
1710 return 0
1711 dur = 5
1712 if len(args) > 2:
1713 dur = float(args[2])
1714 active = False
1715 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: monitorSource(ctx, console.mouse.eventSource, active, dur)])
1716 return 0
1717
1718def monitorVBoxCmd(ctx, args):
1719 if len(args) > 2:
1720 print("usage: monitorVBox (duration)")
1721 return 0
1722 dur = 5
1723 if len(args) > 1:
1724 dur = float(args[1])
1725 vbox = ctx['vb']
1726 active = False
1727 monitorSource(ctx, vbox.eventSource, active, dur)
1728 return 0
1729
1730def getAdapterType(ctx, natype):
1731 if (natype in ( ctx['global'].constants.NetworkAdapterType_Am79C970A
1732 , ctx['global'].constants.NetworkAdapterType_Am79C973
1733 , ctx['global'].constants.NetworkAdapterType_Am79C960)):
1734 return "pcnet"
1735 if (natype in ( ctx['global'].constants.NetworkAdapterType_I82540EM
1736 , ctx['global'].constants.NetworkAdapterType_I82545EM
1737 , ctx['global'].constants.NetworkAdapterType_I82543GC)):
1738 return "e1000"
1739 if natype == ctx['global'].constants.NetworkAdapterType_Virtio:
1740 return "virtio"
1741 if natype == ctx['global'].constants.NetworkAdapterType_Null:
1742 return None
1743 raise Exception("Unknown adapter type: "+natype)
1744
1745def portForwardCmd(ctx, args):
1746 if len(args) != 5:
1747 print("usage: portForward <vmname|uuid> <adapter> <hostPort> <guestPort>")
1748 return 0
1749 mach = argsToMach(ctx, args)
1750 if not mach:
1751 return 0
1752 adapterNum = int(args[2])
1753 hostPort = int(args[3])
1754 guestPort = int(args[4])
1755 proto = "TCP"
1756 session = ctx['global'].openMachineSession(mach, fPermitSharing=True)
1757 mach = session.machine
1758
1759 adapter = mach.getNetworkAdapter(adapterNum)
1760 adapterType = getAdapterType(ctx, adapter.adapterType)
1761
1762 profile_name = proto+"_"+str(hostPort)+"_"+str(guestPort)
1763 config = "VBoxInternal/Devices/" + adapterType + "/"
1764 config = config + str(adapter.slot) +"/LUN#0/Config/" + profile_name
1765
1766 mach.setExtraData(config + "/Protocol", proto)
1767 mach.setExtraData(config + "/HostPort", str(hostPort))
1768 mach.setExtraData(config + "/GuestPort", str(guestPort))
1769
1770 mach.saveSettings()
1771 session.unlockMachine()
1772
1773 return 0
1774
1775
1776def showLogCmd(ctx, args):
1777 if len(args) < 2:
1778 print("usage: showLog <vmname|uuid> <num>")
1779 return 0
1780 mach = argsToMach(ctx, args)
1781 if not mach:
1782 return 0
1783
1784 log = 0
1785 if len(args) > 2:
1786 log = args[2]
1787
1788 uOffset = 0
1789 while True:
1790 data = mach.readLog(log, uOffset, 4096)
1791 if len(data) == 0:
1792 break
1793 # print adds either NL or space to chunks not ending with a NL
1794 sys.stdout.write(str(data))
1795 uOffset += len(data)
1796
1797 return 0
1798
1799def findLogCmd(ctx, args):
1800 if len(args) < 3:
1801 print("usage: findLog <vmname|uuid> <pattern> <num>")
1802 return 0
1803 mach = argsToMach(ctx, args)
1804 if not mach:
1805 return 0
1806
1807 log = 0
1808 if len(args) > 3:
1809 log = args[3]
1810
1811 pattern = args[2]
1812 uOffset = 0
1813 while True:
1814 # to reduce line splits on buffer boundary
1815 data = mach.readLog(log, uOffset, 512*1024)
1816 if len(data) == 0:
1817 break
1818 buf = str(data).split("\n")
1819 for line in buf:
1820 match = re.findall(pattern, line)
1821 if len(match) > 0:
1822 for cur_match in match:
1823 line = line.replace(cur_match, colored(cur_match, 'red'))
1824 print(line)
1825 uOffset += len(data)
1826
1827 return 0
1828
1829
1830def findAssertCmd(ctx, args):
1831 if len(args) < 2:
1832 print("usage: findAssert <vmname|uuid> <num>")
1833 return 0
1834 mach = argsToMach(ctx, args)
1835 if not mach:
1836 return 0
1837
1838 log = 0
1839 if len(args) > 2:
1840 log = args[2]
1841
1842 uOffset = 0
1843 ere = re.compile(r'(Expression:|\!\!\!\!\!\!)')
1844 active = False
1845 context = 0
1846 while True:
1847 # to reduce line splits on buffer boundary
1848 data = mach.readLog(log, uOffset, 512*1024)
1849 if len(data) == 0:
1850 break
1851 buf = str(data).split("\n")
1852 for line in buf:
1853 if active:
1854 print(line)
1855 if context == 0:
1856 active = False
1857 else:
1858 context = context - 1
1859 continue
1860 match = ere.findall(line)
1861 if len(match) > 0:
1862 active = True
1863 context = 50
1864 print(line)
1865 uOffset += len(data)
1866
1867 return 0
1868
1869def evalCmd(ctx, args):
1870 expr = ' '.join(args[1:])
1871 try:
1872 exec(expr) # pylint: disable=exec-used
1873 except Exception as e:
1874 printErr(ctx, e)
1875 if g_fVerbose:
1876 traceback.print_exc()
1877 return 0
1878
1879def reloadExtCmd(ctx, _args):
1880 # maybe will want more args smartness
1881 checkUserExtensions(ctx, commands, getHomeFolder(ctx))
1882 autoCompletion(commands, ctx)
1883 return 0
1884
1885def runScriptCmd(ctx, args):
1886 if len(args) != 2:
1887 print("usage: runScript <script>")
1888 return 0
1889
1890 try:
1891 with open(args[1], 'r', encoding='utf-8') as file:
1892 try:
1893 lines = file.readlines()
1894 ctx['scriptLine'] = 0
1895 ctx['interrupt'] = False
1896 while ctx['scriptLine'] < len(lines):
1897 line = lines[ctx['scriptLine']]
1898 ctx['scriptLine'] = ctx['scriptLine'] + 1
1899 done = runCommand(ctx, line)
1900 if done != 0 or ctx['interrupt']:
1901 break
1902
1903 except Exception as e:
1904 printErr(ctx, e)
1905 if g_fVerbose:
1906 traceback.print_exc()
1907 file.close()
1908 except IOError as e:
1909 print("cannot open:", args[1], ":", e)
1910 return 1
1911 return 0
1912
1913def sleepCmd(_ctx, args):
1914 if len(args) != 2:
1915 print("usage: sleep <secs>")
1916 return 0
1917
1918 try:
1919 time.sleep(float(args[1]))
1920 except:
1921 # to allow sleep interrupt
1922 pass
1923 return 0
1924
1925
1926def shellCmd(_ctx, args):
1927 if len(args) < 2:
1928 print("usage: shell <commands>")
1929 return 0
1930 cmd = ' '.join(args[1:])
1931
1932 try:
1933 os.system(cmd)
1934 except KeyboardInterrupt:
1935 # to allow shell command interruption
1936 pass
1937 return 0
1938
1939
1940def connectCmd(ctx, args):
1941 if len(args) > 4:
1942 print("usage: connect url <username> <passwd>")
1943 return 0
1944
1945 if ctx['vb'] is not None:
1946 print("Already connected, disconnect first...")
1947 return 0
1948
1949 if len(args) > 1:
1950 url = args[1]
1951 else:
1952 url = None
1953
1954 if len(args) > 2:
1955 user = args[2]
1956 else:
1957 user = ""
1958
1959 if len(args) > 3:
1960 passwd = args[3]
1961 else:
1962 passwd = ""
1963
1964 ctx['wsinfo'] = [url, user, passwd]
1965 ctx['vb'] = ctx['global'].platform.connect(url, user, passwd)
1966 try:
1967 print("Running VirtualBox version %s" % (ctx['vb'].version))
1968 except Exception as e:
1969 printErr(ctx, e)
1970 if g_fVerbose:
1971 traceback.print_exc()
1972 ctx['perf'] = ctx['global'].getPerfCollector(ctx['vb'])
1973 return 0
1974
1975def disconnectCmd(ctx, args):
1976 if len(args) != 1:
1977 print("usage: disconnect")
1978 return 0
1979
1980 if ctx['vb'] is None:
1981 print("Not connected yet.")
1982 return 0
1983
1984 try:
1985 ctx['global'].platform.disconnect()
1986 except:
1987 ctx['vb'] = None
1988 raise
1989
1990 ctx['vb'] = None
1991 return 0
1992
1993def reconnectCmd(ctx, _args):
1994 if ctx['wsinfo'] is None:
1995 print("Never connected...")
1996 return 0
1997
1998 try:
1999 ctx['global'].platform.disconnect()
2000 except:
2001 pass
2002
2003 [url, user, passwd] = ctx['wsinfo']
2004 ctx['vb'] = ctx['global'].platform.connect(url, user, passwd)
2005 try:
2006 print("Running VirtualBox version %s" % (ctx['vb'].version))
2007 except Exception as e:
2008 printErr(ctx, e)
2009 if g_fVerbose:
2010 traceback.print_exc()
2011 ctx['perf'] = ctx['global'].getPerfCollector(ctx['vb'])
2012 return 0
2013
2014def exportVMCmd(ctx, args):
2015 if len(args) < 3:
2016 print("usage: exportVm <machine> <path> <format> <license>")
2017 return 0
2018 mach = argsToMach(ctx, args)
2019 if mach is None:
2020 return 0
2021 path = args[2]
2022 if len(args) > 3:
2023 fmt = args[3]
2024 else:
2025 fmt = "ovf-1.0"
2026 if len(args) > 4:
2027 lic = args[4]
2028 else:
2029 lic = "GPL"
2030
2031 app = ctx['vb'].createAppliance()
2032 desc = mach.export(app)
2033 desc.addDescription(ctx['global'].constants.VirtualSystemDescriptionType_License, lic, "")
2034 progress = app.write(fmt, path)
2035 if (progressBar(ctx, progress) and int(progress.resultCode) == 0):
2036 print("Exported to %s in format %s" % (path, fmt))
2037 else:
2038 reportError(ctx, progress)
2039 return 0
2040
2041# PC XT scancodes
2042scancodes = {
2043 'a': 0x1e,
2044 'b': 0x30,
2045 'c': 0x2e,
2046 'd': 0x20,
2047 'e': 0x12,
2048 'f': 0x21,
2049 'g': 0x22,
2050 'h': 0x23,
2051 'i': 0x17,
2052 'j': 0x24,
2053 'k': 0x25,
2054 'l': 0x26,
2055 'm': 0x32,
2056 'n': 0x31,
2057 'o': 0x18,
2058 'p': 0x19,
2059 'q': 0x10,
2060 'r': 0x13,
2061 's': 0x1f,
2062 't': 0x14,
2063 'u': 0x16,
2064 'v': 0x2f,
2065 'w': 0x11,
2066 'x': 0x2d,
2067 'y': 0x15,
2068 'z': 0x2c,
2069 '0': 0x0b,
2070 '1': 0x02,
2071 '2': 0x03,
2072 '3': 0x04,
2073 '4': 0x05,
2074 '5': 0x06,
2075 '6': 0x07,
2076 '7': 0x08,
2077 '8': 0x09,
2078 '9': 0x0a,
2079 ' ': 0x39,
2080 '-': 0xc,
2081 '=': 0xd,
2082 '[': 0x1a,
2083 ']': 0x1b,
2084 ';': 0x27,
2085 '\'': 0x28,
2086 ',': 0x33,
2087 '.': 0x34,
2088 '/': 0x35,
2089 '\t': 0xf,
2090 '\n': 0x1c,
2091 '`': 0x29
2092}
2093
2094extScancodes = {
2095 'ESC' : [0x01],
2096 'BKSP': [0xe],
2097 'SPACE': [0x39],
2098 'TAB': [0x0f],
2099 'CAPS': [0x3a],
2100 'ENTER': [0x1c],
2101 'LSHIFT': [0x2a],
2102 'RSHIFT': [0x36],
2103 'INS': [0xe0, 0x52],
2104 'DEL': [0xe0, 0x53],
2105 'END': [0xe0, 0x4f],
2106 'HOME': [0xe0, 0x47],
2107 'PGUP': [0xe0, 0x49],
2108 'PGDOWN': [0xe0, 0x51],
2109 'LGUI': [0xe0, 0x5b], # GUI, aka Win, aka Apple key
2110 'RGUI': [0xe0, 0x5c],
2111 'LCTR': [0x1d],
2112 'RCTR': [0xe0, 0x1d],
2113 'LALT': [0x38],
2114 'RALT': [0xe0, 0x38],
2115 'APPS': [0xe0, 0x5d],
2116 'F1': [0x3b],
2117 'F2': [0x3c],
2118 'F3': [0x3d],
2119 'F4': [0x3e],
2120 'F5': [0x3f],
2121 'F6': [0x40],
2122 'F7': [0x41],
2123 'F8': [0x42],
2124 'F9': [0x43],
2125 'F10': [0x44 ],
2126 'F11': [0x57],
2127 'F12': [0x58],
2128 'UP': [0xe0, 0x48],
2129 'LEFT': [0xe0, 0x4b],
2130 'DOWN': [0xe0, 0x50],
2131 'RIGHT': [0xe0, 0x4d],
2132}
2133
2134def keyDown(ch):
2135 code = scancodes.get(ch, 0x0)
2136 if code != 0:
2137 return [code]
2138 extCode = extScancodes.get(ch, [])
2139 if len(extCode) == 0:
2140 print("bad ext", ch)
2141 return extCode
2142
2143def keyUp(ch):
2144 codes = keyDown(ch)[:] # make a copy
2145 if len(codes) > 0:
2146 codes[len(codes)-1] += 0x80
2147 return codes
2148
2149def typeInGuest(console, text, delay):
2150 pressed = []
2151 group = False
2152 modGroupEnd = True
2153 i = 0
2154 kbd = console.keyboard
2155 while i < len(text):
2156 ch = text[i]
2157 i = i+1
2158 if ch == '{':
2159 # start group, all keys to be pressed at the same time
2160 group = True
2161 continue
2162 if ch == '}':
2163 # end group, release all keys
2164 for c in pressed:
2165 kbd.putScancodes(keyUp(c))
2166 pressed = []
2167 group = False
2168 continue
2169 if ch == 'W':
2170 # just wait a bit
2171 time.sleep(0.3)
2172 continue
2173 if ch in ('^', '|', '$', '_'):
2174 if ch == '^':
2175 ch = 'LCTR'
2176 if ch == '|':
2177 ch = 'LSHIFT'
2178 if ch == '_':
2179 ch = 'LALT'
2180 if ch == '$':
2181 ch = 'LGUI'
2182 if not group:
2183 modGroupEnd = False
2184 else:
2185 if ch == '\\':
2186 if i < len(text):
2187 ch = text[i]
2188 i = i+1
2189 if ch == 'n':
2190 ch = '\n'
2191 elif ch == '&':
2192 combo = ""
2193 while i < len(text):
2194 ch = text[i]
2195 i = i+1
2196 if ch == ';':
2197 break
2198 combo += ch
2199 ch = combo
2200 modGroupEnd = True
2201 kbd.putScancodes(keyDown(ch))
2202 pressed.insert(0, ch)
2203 if not group and modGroupEnd:
2204 for c in pressed:
2205 kbd.putScancodes(keyUp(c))
2206 pressed = []
2207 modGroupEnd = True
2208 time.sleep(delay)
2209
2210def typeGuestCmd(ctx, args):
2211 if len(args) < 3:
2212 print("usage: typeGuest <machine> <text> <charDelay>")
2213 return 0
2214 mach = argsToMach(ctx, args)
2215 if mach is None:
2216 return 0
2217
2218 text = args[2]
2219
2220 if len(args) > 3:
2221 delay = float(args[3])
2222 else:
2223 delay = 0.1
2224
2225 gargs = [lambda ctx, mach, console, args: typeInGuest(console, text, delay)]
2226 cmdExistingVm(ctx, mach, 'guestlambda', gargs)
2227
2228 return 0
2229
2230def optId(verbose, uuid):
2231 if verbose:
2232 return ": "+uuid
2233 return ""
2234
2235def asSize(val, inBytes):
2236 if inBytes:
2237 return int(val)/(1024*1024)
2238 return int(val)
2239
2240def listMediaCmd(ctx, args):
2241 if len(args) > 1:
2242 verbose = int(args[1])
2243 else:
2244 verbose = False
2245 hdds = ctx['global'].getArray(ctx['vb'], 'hardDisks')
2246 print(colCat(ctx, "Hard disks:"))
2247 for hdd in hdds:
2248 if hdd.state != ctx['global'].constants.MediumState_Created:
2249 hdd.refreshState()
2250 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))))
2251
2252 dvds = ctx['global'].getArray(ctx['vb'], 'DVDImages')
2253 print(colCat(ctx, "CD/DVD disks:"))
2254 for dvd in dvds:
2255 if dvd.state != ctx['global'].constants.MediumState_Created:
2256 dvd.refreshState()
2257 print(" %s (%s)%s %s" % (colPath(ctx, dvd.location), dvd.format, optId(verbose, dvd.id), colSizeM(ctx, asSize(dvd.size, True))))
2258
2259 floppys = ctx['global'].getArray(ctx['vb'], 'floppyImages')
2260 print(colCat(ctx, "Floppy disks:"))
2261 for floppy in floppys:
2262 if floppy.state != ctx['global'].constants.MediumState_Created:
2263 floppy.refreshState()
2264 print(" %s (%s)%s %s" % (colPath(ctx, floppy.location), floppy.format, optId(verbose, floppy.id), colSizeM(ctx, asSize(floppy.size, True))))
2265
2266 return 0
2267
2268def listUsbCmd(ctx, args):
2269 if len(args) > 1:
2270 print("usage: listUsb")
2271 return 0
2272
2273 host = ctx['vb'].host
2274 for usbdev in ctx['global'].getArray(host, 'USBDevices'):
2275 printHostUsbDev(ctx, usbdev)
2276
2277 return 0
2278
2279def findDevOfType(ctx, mach, devtype):
2280 attachments = ctx['global'].getArray(mach, 'mediumAttachments')
2281 for att in attachments:
2282 if att.type == devtype:
2283 return [att.controller, att.port, att.device]
2284 return [None, 0, 0]
2285
2286def createHddCmd(ctx, args):
2287 if len(args) < 3:
2288 print("usage: createHdd sizeM location type")
2289 return 0
2290
2291 size = int(args[1])
2292 loc = args[2]
2293 if len(args) > 3:
2294 fmt = args[3]
2295 else:
2296 fmt = "vdi"
2297
2298 hdd = ctx['vb'].createMedium(fmt, loc, ctx['global'].constants.AccessMode_ReadWrite, ctx['global'].constants.DeviceType_HardDisk)
2299 progress = hdd.createBaseStorage(size, (ctx['global'].constants.MediumVariant_Standard, ))
2300 if progressBar(ctx,progress) and hdd.id:
2301 print("created HDD at %s as %s" % (colPath(ctx,hdd.location), hdd.id))
2302 else:
2303 print("cannot create disk (file %s exist?)" % (loc))
2304 reportError(ctx,progress)
2305 return 0
2306
2307 return 0
2308
2309def registerHddCmd(ctx, args):
2310 if len(args) < 2:
2311 print("usage: registerHdd location")
2312 return 0
2313
2314 vbox = ctx['vb']
2315 loc = args[1]
2316 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2317 print("registered HDD as %s" % (hdd.id))
2318 return 0
2319
2320def controldevice(_ctx, mach, args):
2321 [ctr, port, slot, devtype, uuid] = args
2322 mach.attachDevice(ctr, port, slot, devtype, uuid)
2323
2324def attachHddCmd(ctx, args):
2325 if len(args) < 3:
2326 print("usage: attachHdd <vmname|uuid> <hdd> <controller> <port:slot>")
2327 return 0
2328
2329 mach = argsToMach(ctx, args)
2330 if mach is None:
2331 return 0
2332 vbox = ctx['vb']
2333 loc = args[2]
2334 try:
2335 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2336 except:
2337 print("no HDD with path %s registered" % (loc))
2338 return 0
2339 if len(args) > 3:
2340 ctr = args[3]
2341 (port, slot) = args[4].split(":")
2342 else:
2343 [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_HardDisk)
2344
2345 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.attachDevice(ctr, port, slot, ctx['global'].constants.DeviceType_HardDisk, hdd.id))
2346 return 0
2347
2348def detachVmDevice(ctx, mach, args):
2349 attachments = ctx['global'].getArray(mach, 'mediumAttachments')
2350 hid = args[0]
2351 for att in attachments:
2352 if att.medium:
2353 if hid in ('ALL', att.medium.id):
2354 mach.detachDevice(att.controller, att.port, att.device)
2355
2356def detachMedium(ctx, mid, medium):
2357 cmdClosedVm(ctx, machById(ctx, mid), detachVmDevice, [medium])
2358
2359def detachHddCmd(ctx, args):
2360 if len(args) < 3:
2361 print("usage: detachHdd <vmname|uuid> <hdd>")
2362 return 0
2363
2364 mach = argsToMach(ctx, args)
2365 if mach is None:
2366 return 0
2367 vbox = ctx['vb']
2368 loc = args[2]
2369 try:
2370 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2371 except:
2372 print("no HDD with path %s registered" % (loc))
2373 return 0
2374
2375 detachMedium(ctx, mach.id, hdd)
2376 return 0
2377
2378def unregisterHddCmd(ctx, args):
2379 if len(args) < 2:
2380 print("usage: unregisterHdd path <vmunreg>")
2381 return 0
2382
2383 vbox = ctx['vb']
2384 loc = args[1]
2385 if len(args) > 2:
2386 vmunreg = int(args[2])
2387 else:
2388 vmunreg = 0
2389 try:
2390 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2391 except:
2392 print("no HDD with path %s registered" % (loc))
2393 return 0
2394
2395 if vmunreg != 0:
2396 machs = ctx['global'].getArray(hdd, 'machineIds')
2397 try:
2398 for mach in machs:
2399 print("Trying to detach from %s" % (mach))
2400 detachMedium(ctx, mach, hdd)
2401 except Exception as e:
2402 print('failed: ', e)
2403 return 0
2404 hdd.close()
2405 return 0
2406
2407def removeHddCmd(ctx, args):
2408 if len(args) != 2:
2409 print("usage: removeHdd path")
2410 return 0
2411
2412 vbox = ctx['vb']
2413 loc = args[1]
2414 try:
2415 hdd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_HardDisk, ctx['global'].constants.AccessMode_ReadWrite, False)
2416 except:
2417 print("no HDD with path %s registered" % (loc))
2418 return 0
2419
2420 progress = hdd.deleteStorage()
2421 progressBar(ctx, progress)
2422
2423 return 0
2424
2425def registerIsoCmd(ctx, args):
2426 if len(args) < 2:
2427 print("usage: registerIso location")
2428 return 0
2429
2430 vbox = ctx['vb']
2431 loc = args[1]
2432 iso = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2433 print("registered ISO as %s" % (iso.id))
2434 return 0
2435
2436def unregisterIsoCmd(ctx, args):
2437 if len(args) != 2:
2438 print("usage: unregisterIso path")
2439 return 0
2440
2441 vbox = ctx['vb']
2442 loc = args[1]
2443 try:
2444 vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2445 except:
2446 print("no DVD with path %s registered" % (loc))
2447 return 0
2448
2449 print("Unregistered ISO at %s" % (colPath(ctx, loc)))
2450 return 0
2451
2452def removeIsoCmd(ctx, args):
2453 if len(args) != 2:
2454 print("usage: removeIso path")
2455 return 0
2456
2457 vbox = ctx['vb']
2458 loc = args[1]
2459 try:
2460 dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2461 except:
2462 print("no DVD with path %s registered" % (loc))
2463 return 0
2464
2465 progress = dvd.deleteStorage()
2466 if progressBar(ctx, progress):
2467 print("Removed ISO at %s" % (colPath(ctx, dvd.location)))
2468 else:
2469 reportError(ctx, progress)
2470 return 0
2471
2472def attachIsoCmd(ctx, args):
2473 if len(args) < 3:
2474 print("usage: attachIso <vmname|uuid> <iso> <controller> <port:slot>")
2475 return 0
2476
2477 mach = argsToMach(ctx, args)
2478 if mach is None:
2479 return 0
2480 vbox = ctx['vb']
2481 loc = args[2]
2482 try:
2483 dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2484 except:
2485 print("no DVD with path %s registered" % (loc))
2486 return 0
2487 if len(args) > 3:
2488 ctr = args[3]
2489 (port, slot) = args[4].split(":")
2490 else:
2491 [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_DVD)
2492 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.attachDevice(ctr, port, slot, ctx['global'].constants.DeviceType_DVD, dvd))
2493 return 0
2494
2495def detachIsoCmd(ctx, args):
2496 if len(args) < 3:
2497 print("usage: detachIso <vmname|uuid> <iso>")
2498 return 0
2499
2500 mach = argsToMach(ctx, args)
2501 if mach is None:
2502 return 0
2503 vbox = ctx['vb']
2504 loc = args[2]
2505 try:
2506 dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2507 except:
2508 print("no DVD with path %s registered" % (loc))
2509 return 0
2510
2511 detachMedium(ctx, mach.id, dvd)
2512 return 0
2513
2514def mountIsoCmd(ctx, args):
2515 if len(args) < 3:
2516 print("usage: mountIso <vmname|uuid> <iso> <controller> <port:slot>")
2517 return 0
2518
2519 mach = argsToMach(ctx, args)
2520 if mach is None:
2521 return 0
2522 vbox = ctx['vb']
2523 loc = args[2]
2524 try:
2525 dvd = vbox.openMedium(loc, ctx['global'].constants.DeviceType_DVD, ctx['global'].constants.AccessMode_ReadOnly, False)
2526 except:
2527 print("no DVD with path %s registered" % (loc))
2528 return 0
2529
2530 if len(args) > 3:
2531 ctr = args[3]
2532 (port, slot) = args[4].split(":")
2533 else:
2534 # autodetect controller and location, just find first controller with media == DVD
2535 [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_DVD)
2536
2537 cmdExistingVm(ctx, mach, 'mountiso', [ctr, port, slot, dvd, True])
2538
2539 return 0
2540
2541def unmountIsoCmd(ctx, args):
2542 if len(args) < 2:
2543 print("usage: unmountIso <vmname|uuid> <controller> <port:slot>")
2544 return 0
2545
2546 mach = argsToMach(ctx, args)
2547 if mach is None:
2548 return 0
2549
2550 if len(args) > 3:
2551 ctr = args[2]
2552 (port, slot) = args[3].split(":")
2553 else:
2554 # autodetect controller and location, just find first controller with media == DVD
2555 [ctr, port, slot] = findDevOfType(ctx, mach, ctx['global'].constants.DeviceType_DVD)
2556
2557 cmdExistingVm(ctx, mach, 'mountiso', [ctr, port, slot, None, True])
2558
2559 return 0
2560
2561def attachCtr(_ctx, mach, args):
2562 [name, bus, ctrltype] = args
2563 ctr = mach.addStorageController(name, bus)
2564 if ctrltype:
2565 ctr.controllerType = ctrltype
2566
2567def attachCtrCmd(ctx, args):
2568 if len(args) < 4:
2569 print("usage: attachCtr <vmname|uuid> <controller name> <bus> <type>")
2570 return 0
2571
2572 if len(args) > 4:
2573 ctrltype = enumFromString(ctx, 'StorageControllerType', args[4])
2574 if not ctrltype:
2575 print("Controller type %s unknown" % (args[4]))
2576 return 0
2577 else:
2578 ctrltype = None
2579
2580 mach = argsToMach(ctx, args)
2581 if mach is None:
2582 return 0
2583 bus = enumFromString(ctx, 'StorageBus', args[3])
2584 if bus is None:
2585 print("Bus type %s unknown" % (args[3]))
2586 return 0
2587 name = args[2]
2588 cmdClosedVm(ctx, mach, attachCtr, [name, bus, ctrltype])
2589 return 0
2590
2591def detachCtrCmd(ctx, args):
2592 if len(args) < 3:
2593 print("usage: detachCtr <vmname|uuid> <controller name>")
2594 return 0
2595
2596 mach = argsToMach(ctx, args)
2597 if mach is None:
2598 return 0
2599 ctr = args[2]
2600 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.removeStorageController(ctr))
2601 return 0
2602
2603def usbctr(_ctx, _mach, console, args):
2604 if args[0]:
2605 console.attachUSBDevice(args[1], "")
2606 else:
2607 console.detachUSBDevice(args[1])
2608
2609def attachUsbCmd(ctx, args):
2610 if len(args) < 3:
2611 print("usage: attachUsb <vmname|uuid> <device uid>")
2612 return 0
2613
2614 mach = argsToMach(ctx, args)
2615 if mach is None:
2616 return 0
2617 dev = args[2]
2618 cmdExistingVm(ctx, mach, 'guestlambda', [usbctr, True, dev])
2619 return 0
2620
2621def detachUsbCmd(ctx, args):
2622 if len(args) < 3:
2623 print("usage: detachUsb <vmname|uuid> <device uid>")
2624 return 0
2625
2626 mach = argsToMach(ctx, args)
2627 if mach is None:
2628 return 0
2629 dev = args[2]
2630 cmdExistingVm(ctx, mach, 'guestlambda', [usbctr, False, dev])
2631 return 0
2632
2633
2634def guiCmd(ctx, args):
2635 if len(args) > 1:
2636 print("usage: gui")
2637 return 0
2638
2639 binDir = ctx['global'].getBinDir()
2640
2641 vbox = os.path.join(binDir, 'VirtualBox')
2642 try:
2643 os.system(vbox)
2644 except KeyboardInterrupt:
2645 # to allow interruption
2646 pass
2647 return 0
2648
2649def shareFolderCmd(ctx, args):
2650 if len(args) < 4:
2651 print("usage: shareFolder <vmname|uuid> <path> <name> <writable|persistent>")
2652 return 0
2653
2654 mach = argsToMach(ctx, args)
2655 if mach is None:
2656 return 0
2657 path = args[2]
2658 name = args[3]
2659 writable = False
2660 persistent = False
2661 if len(args) > 4:
2662 for cur_arg in args[4:]:
2663 if cur_arg == 'writable':
2664 writable = True
2665 if cur_arg == 'persistent':
2666 persistent = True
2667 if persistent:
2668 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.createSharedFolder(name, path, writable), [])
2669 else:
2670 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: console.createSharedFolder(name, path, writable)])
2671 return 0
2672
2673def unshareFolderCmd(ctx, args):
2674 if len(args) < 3:
2675 print("usage: unshareFolder <vmname|uuid> <name>")
2676 return 0
2677
2678 mach = argsToMach(ctx, args)
2679 if mach is None:
2680 return 0
2681 name = args[2]
2682 found = False
2683 for sharedfolder in ctx['global'].getArray(mach, 'sharedFolders'):
2684 if sharedfolder.name == name:
2685 cmdClosedVm(ctx, mach, lambda ctx, mach, args: mach.removeSharedFolder(name), [])
2686 found = True
2687 break
2688 if not found:
2689 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: console.removeSharedFolder(name)])
2690 return 0
2691
2692
2693def snapshotCmd(ctx, args):
2694 if (len(args) < 2 or args[1] == 'help'):
2695 print("Take snapshot: snapshot <vmname|uuid> take <name> <description>")
2696 print("Restore snapshot: snapshot <vmname|uuid> restore <name>")
2697 print("Merge snapshot: snapshot <vmname|uuid> merge <name>")
2698 return 0
2699
2700 mach = argsToMach(ctx, args)
2701 if mach is None:
2702 return 0
2703 cmd = args[2]
2704 if cmd == 'take':
2705 if len(args) < 4:
2706 print("usage: snapshot <vmname|uuid> take <name> <description>")
2707 return 0
2708 name = args[3]
2709 if len(args) > 4:
2710 desc = args[4]
2711 else:
2712 desc = ""
2713 cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.takeSnapshot(name, desc, True)[0]))
2714 return 0
2715
2716 if cmd == 'restore':
2717 if len(args) < 4:
2718 print("usage: snapshot <vmname|uuid> restore <name>")
2719 return 0
2720 name = args[3]
2721 snap = mach.findSnapshot(name)
2722 cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.restoreSnapshot(snap)))
2723 return 0
2724
2725 if cmd == 'restorecurrent':
2726 if len(args) < 4:
2727 print("usage: snapshot <vmname|uuid> restorecurrent")
2728 return 0
2729 snap = mach.currentSnapshot()
2730 cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.restoreSnapshot(snap)))
2731 return 0
2732
2733 if cmd == 'delete':
2734 if len(args) < 4:
2735 print("usage: snapshot <vmname|uuid> delete <name>")
2736 return 0
2737 name = args[3]
2738 snap = mach.findSnapshot(name)
2739 cmdAnyVm(ctx, mach, lambda ctx, mach, console, args: progressBar(ctx, mach.deleteSnapshot(snap.id)))
2740 return 0
2741
2742 print("Command '%s' is unknown" % (cmd))
2743 return 0
2744
2745def natAlias(_ctx, _mach, _nicnum, nat, args=None):
2746 """This command shows/alters NAT's alias settings.
2747 usage: nat <vmname|uuid> <nicnum> alias [default|[log] [proxyonly] [sameports]]
2748 default - set settings to default values
2749 log - switch on alias logging
2750 proxyonly - switch proxyonly mode on
2751 sameports - enforces NAT using the same ports
2752 """
2753 alias = {
2754 'log': 0x1,
2755 'proxyonly': 0x2,
2756 'sameports': 0x4
2757 }
2758 if len(args) == 1:
2759 first = 0
2760 msg = ''
2761 for aliasmode, aliaskey in list(alias.items()):
2762 if first == 0:
2763 first = 1
2764 else:
2765 msg += ', '
2766 if int(nat.aliasMode) & aliaskey:
2767 msg += '%s: %s' % (aliasmode, 'on')
2768 else:
2769 msg += '%s: %s' % (aliasmode, 'off')
2770 return (0, [msg])
2771
2772 nat.aliasMode = 0
2773 if 'default' not in args:
2774 for idx in range(1, len(args)):
2775 if args[idx] not in alias:
2776 print('Invalid alias mode: ' + args[idx])
2777 print(natAlias.__doc__)
2778 return (1, None)
2779 nat.aliasMode = int(nat.aliasMode) | alias[args[idx]]
2780 return (0, None)
2781
2782def natSettings(_ctx, _mach, _nicnum, nat, args):
2783 """
2784 This command shows/alters NAT settings.
2785 usage: nat <vmname|uuid> <nicnum> settings [<mtu> [[<socsndbuf> <sockrcvbuf> [<tcpsndwnd> <tcprcvwnd>]]]]
2786 mtu - set mtu <= 16000
2787 socksndbuf/sockrcvbuf - sets amount of kb for socket sending/receiving buffer
2788 tcpsndwnd/tcprcvwnd - sets size of initial tcp sending/receiving window
2789 """
2790 if len(args) == 1:
2791 (mtu, socksndbuf, sockrcvbuf, tcpsndwnd, tcprcvwnd) = nat.getNetworkSettings()
2792 if mtu == 0: mtu = 1500
2793 if socksndbuf == 0: socksndbuf = 64
2794 if sockrcvbuf == 0: sockrcvbuf = 64
2795 if tcpsndwnd == 0: tcpsndwnd = 64
2796 if tcprcvwnd == 0: tcprcvwnd = 64
2797 msg = 'mtu:%s socket(snd:%s, rcv:%s) tcpwnd(snd:%s, rcv:%s)' % (mtu, socksndbuf, sockrcvbuf, tcpsndwnd, tcprcvwnd)
2798 return (0, [msg])
2799
2800 if args[1] < 16000:
2801 print('invalid mtu value (%s not in range [65 - 16000])' % (args[1]))
2802 return (1, None)
2803 for i in range(2, len(args)):
2804 if not args[i].isdigit() or int(args[i]) < 8 or int(args[i]) > 1024:
2805 print('invalid %s parameter (%i not in range [8-1024])' % (i, args[i]))
2806 return (1, None)
2807 nic_args = [args[1]]
2808 if len(args) < 6:
2809 for i in range(2, len(args)): nic_args.append(args[i])
2810 for i in range(len(args), 6): nic_args.append(0)
2811 else:
2812 for i in range(2, len(args)): nic_args.append(args[i])
2813 #print(a)
2814 nat.setNetworkSettings(int(nic_args[0]), int(nic_args[1]), int(nic_args[2]), int(nic_args[3]), int(nic_args[4]))
2815 return (0, None)
2816
2817def natDns(_ctx, _mach, _nicnum, nat, args):
2818 """This command shows/alters DNS's NAT settings
2819 usage: nat <vmname|uuid> <nicnum> dns [passdomain] [proxy] [usehostresolver]
2820 passdomain - enforces builtin DHCP server to pass domain
2821 proxy - switch on builtin NAT DNS proxying mechanism
2822 usehostresolver - proxies all DNS requests to Host Resolver interface
2823 """
2824 yesno = {0: 'off', 1: 'on'}
2825 if len(args) == 1:
2826 msg = 'passdomain:%s, proxy:%s, usehostresolver:%s' % (yesno[int(nat.DNSPassDomain)], yesno[int(nat.DNSProxy)], yesno[int(nat.DNSUseHostResolver)])
2827 return (0, [msg])
2828
2829 nat.DNSPassDomain = 'passdomain' in args
2830 nat.DNSProxy = 'proxy' in args
2831 nat.DNSUseHostResolver = 'usehostresolver' in args
2832 return (0, None)
2833
2834def natTftp(ctx, mach, nicnum, nat, args):
2835 """This command shows/alters TFTP settings
2836 usage nat <vmname|uuid> <nicnum> tftp [prefix <prefix>| bootfile <bootfile>| server <server>]
2837 prefix - alters prefix TFTP settings
2838 bootfile - alters bootfile TFTP settings
2839 server - sets booting server
2840 """
2841 if len(args) == 1:
2842 server = nat.TFTPNextServer
2843 if server is None:
2844 server = nat.network
2845 if server is None:
2846 server = '10.0.%d/24' % (int(nicnum) + 2)
2847 (server, _mask) = server.split('/')
2848 while server.count('.') != 3:
2849 server += '.0'
2850 (ipA, ipB, ipC, _ipD) = server.split('.')
2851 server = '%d.%d.%d.4' % (ipA, ipB, ipC)
2852 prefix = nat.TFTPPrefix
2853 if prefix is None:
2854 prefix = '%s/TFTP/' % (ctx['vb'].homeFolder)
2855 bootfile = nat.TFTPBootFile
2856 if bootfile is None:
2857 bootfile = '%s.pxe' % (mach.name)
2858 msg = 'server:%s, prefix:%s, bootfile:%s' % (server, prefix, bootfile)
2859 return (0, [msg])
2860
2861 cmd = args[1]
2862 if len(args) != 3:
2863 print('invalid args:', args)
2864 print(natTftp.__doc__)
2865 return (1, None)
2866 if cmd == 'prefix': nat.TFTPPrefix = args[2]
2867 elif cmd == 'bootfile': nat.TFTPBootFile = args[2]
2868 elif cmd == 'server': nat.TFTPNextServer = args[2]
2869 else:
2870 print("invalid cmd:", cmd)
2871 return (1, None)
2872 return (0, None)
2873
2874def natPortForwarding(ctx, _mach, _nicnum, nat, args):
2875 """This command shows/manages port-forwarding settings
2876 usage:
2877 nat <vmname|uuid> <nicnum> <pf> [ simple tcp|udp <hostport> <guestport>]
2878 |[no_name tcp|udp <hostip> <hostport> <guestip> <guestport>]
2879 |[ex tcp|udp <pf-name> <hostip> <hostport> <guestip> <guestport>]
2880 |[delete <pf-name>]
2881 """
2882 if len(args) == 1:
2883 # note: keys/values are swapped in defining part of the function
2884 proto = {0: 'udp', 1: 'tcp'}
2885 msg = []
2886 port_forwardings = ctx['global'].getArray(nat, 'redirects')
2887 for forwarding in port_forwardings:
2888 (pfnme, pfp, pfhip, pfhp, pfgip, pfgp) = str(forwarding).split(', ')
2889 msg.append('%s: %s %s:%s => %s:%s' % (pfnme, proto[int(pfp)], pfhip, pfhp, pfgip, pfgp))
2890 return (0, msg) # msg is array
2891
2892 proto = {'udp': 0, 'tcp': 1}
2893 pfcmd = {
2894 'simple': {
2895 'validate': lambda: args[1] in list(pfcmd) and args[2] in list(proto) and len(args) == 5,
2896 'func':lambda: nat.addRedirect('', proto[args[2]], '', int(args[3]), '', int(args[4]))
2897 },
2898 'no_name': {
2899 'validate': lambda: args[1] in list(pfcmd) and args[2] in list(proto) and len(args) == 7,
2900 'func': lambda: nat.addRedirect('', proto[args[2]], args[3], int(args[4]), args[5], int(args[6]))
2901 },
2902 'ex': {
2903 'validate': lambda: args[1] in list(pfcmd) and args[2] in list(proto) and len(args) == 8,
2904 'func': lambda: nat.addRedirect(args[3], proto[args[2]], args[4], int(args[5]), args[6], int(args[7]))
2905 },
2906 'delete': {
2907 'validate': lambda: len(args) == 3,
2908 'func': lambda: nat.removeRedirect(args[2])
2909 }
2910 }
2911
2912 if not pfcmd[args[1]]['validate']():
2913 print('invalid port-forwarding or args of sub command ', args[1])
2914 print(natPortForwarding.__doc__)
2915 return (1, None)
2916
2917 _not_sure_for_what_this_is = pfcmd[args[1]]['func']()
2918 return (0, None)
2919
2920def natNetwork(_ctx, _mach, nicnum, nat, args):
2921 """This command shows/alters NAT network settings
2922 usage: nat <vmname|uuid> <nicnum> network [<network>]
2923 """
2924 if len(args) == 1:
2925 if nat.network is not None and len(str(nat.network)) != 0:
2926 msg = '\'%s\'' % (nat.network)
2927 else:
2928 msg = '10.0.%d.0/24' % (int(nicnum) + 2)
2929 return (0, [msg])
2930
2931 (addr, mask) = args[1].split('/')
2932 if addr.count('.') > 3 or int(mask) < 0 or int(mask) > 32:
2933 print('Invalid arguments')
2934 return (1, None)
2935 nat.network = args[1]
2936 return (0, None)
2937
2938def natCmd(ctx, args):
2939 """This command is entry point to NAT settins management
2940 usage: nat <vmname|uuid> <nicnum> <cmd> <cmd-args>
2941 cmd - [alias|settings|tftp|dns|pf|network]
2942 for more information about commands:
2943 nat help <cmd>
2944 """
2945
2946 natcommands = {
2947 'alias' : natAlias,
2948 'settings' : natSettings,
2949 'tftp': natTftp,
2950 'dns': natDns,
2951 'pf': natPortForwarding,
2952 'network': natNetwork
2953 }
2954
2955 if len(args) < 2 or args[1] == 'help':
2956 if len(args) > 2:
2957 print(natcommands[args[2]].__doc__)
2958 else:
2959 print(natCmd.__doc__)
2960 return 0
2961 if len(args) == 1 or len(args) < 4 or args[3] not in natcommands:
2962 print(natCmd.__doc__)
2963 return 0
2964 mach = argsToMach(ctx, args)
2965 if not mach:
2966 print("please specify vm")
2967 return 0
2968 platformProps = mach.platform.properties
2969 if len(args) < 3 or not args[2].isdigit() or int(args[2]) not in list(range(0, platformProps.getMaxNetworkAdapters(mach.platform.chipsetType))):
2970 print('please specify adapter num %d isn\'t in range [0-%d]' % (args[2], platformProps.getMaxNetworkAdapters(mach.platform.chipsetType)))
2971 return 0
2972 nicnum = int(args[2])
2973 cmdargs = []
2974 for i in range(3, len(args)):
2975 cmdargs.append(args[i])
2976
2977 # @todo vvl if nicnum is missed but command is entered
2978 # use NAT func for every adapter on machine.
2979 func = args[3]
2980 rosession = 1
2981 session = None
2982 if len(cmdargs) > 1:
2983 rosession = 0
2984 session = ctx['global'].openMachineSession(mach, fPermitSharing=False)
2985 mach = session.machine
2986
2987 adapter = mach.getNetworkAdapter(nicnum)
2988 natEngine = adapter.NATEngine
2989 (rc, reports) = natcommands[func](ctx, mach, nicnum, natEngine, cmdargs)
2990 if rosession == 0:
2991 if rc == 0:
2992 mach.saveSettings()
2993 session.unlockMachine()
2994 elif reports:
2995 for cur_report in reports:
2996 msg ='%s nic%d %s: %s' % (mach.name, nicnum, func, cur_report)
2997 print(msg)
2998 return 0
2999
3000def nicSwitchOnOff(adapter, attr, args):
3001 if len(args) == 1:
3002 yesno = {0: 'off', 1: 'on'}
3003 resp = yesno[int(adapter.getattr(attr))]
3004 return (0, resp)
3005
3006 yesno = {'off' : 0, 'on' : 1}
3007 if args[1] not in yesno:
3008 print('%s isn\'t acceptable, please choose %s' % (args[1], list(yesno.keys())))
3009 return (1, None)
3010 adapter.setsetattr(attr, yesno[args[1]])
3011 return (0, None)
3012
3013def nicTraceSubCmd(_ctx, _vm, _nicnum, adapter, args):
3014 '''
3015 usage: nic <vmname|uuid> <nicnum> trace [on|off [file]]
3016 '''
3017 (rc, resp) = nicSwitchOnOff(adapter, 'traceEnabled', args)
3018 if len(args) == 1 and rc == 0:
3019 resp = '%s file:%s' % (resp, adapter.traceFile)
3020 return (0, resp)
3021 if len(args) == 3 and rc == 0:
3022 adapter.traceFile = args[2]
3023 return (0, None)
3024
3025def nicLineSpeedSubCmd(_ctx, _vm, _nicnum, adapter, args):
3026 if len(args) == 1:
3027 resp = '%d kbps'% (adapter.lineSpeed)
3028 return (0, resp)
3029
3030 if not args[1].isdigit():
3031 print('%s isn\'t a number' % (args[1]))
3032 return (1, None)
3033 adapter.lineSpeed = int(args[1])
3034 return (0, None)
3035
3036def nicCableSubCmd(_ctx, _vm, _nicnum, adapter, args):
3037 '''
3038 usage: nic <vmname|uuid> <nicnum> cable [on|off]
3039 '''
3040 return nicSwitchOnOff(adapter, 'cableConnected', args)
3041
3042def nicEnableSubCmd(_ctx, _vm, _nicnum, adapter, args):
3043 '''
3044 usage: nic <vmname|uuid> <nicnum> enable [on|off]
3045 '''
3046 return nicSwitchOnOff(adapter, 'enabled', args)
3047
3048def nicTypeSubCmd(ctx, _vm, _nicnum, adapter, args):
3049 '''
3050 usage: nic <vmname|uuid> <nicnum> type [Am79c970A|Am79c970A|I82540EM|I82545EM|I82543GC|Virtio]
3051 '''
3052 if len(args) == 1:
3053 nictypes = ctx['const'].all_values('NetworkAdapterType')
3054 for key in list(nictypes.keys()):
3055 if str(adapter.adapterType) == str(nictypes[key]):
3056 return (0, str(key))
3057 return (1, None)
3058
3059 nictypes = ctx['const'].all_values('NetworkAdapterType')
3060 if args[1] not in list(nictypes.keys()):
3061 print('%s not in acceptable values (%s)' % (args[1], list(nictypes.keys())))
3062 return (1, None)
3063 adapter.adapterType = nictypes[args[1]]
3064 return (0, None)
3065
3066def nicAttachmentSubCmd(ctx, _vm, _nicnum, adapter, args):
3067 '''
3068 usage: nic <vmname|uuid> <nicnum> attachment [Null|NAT|Bridged <interface>|Internal <name>|HostOnly <interface>
3069 '''
3070 if len(args) == 1:
3071 nicAttachmentType = {
3072 ctx['global'].constants.NetworkAttachmentType_Null: ('Null', ''),
3073 ctx['global'].constants.NetworkAttachmentType_NAT: ('NAT', ''),
3074 ctx['global'].constants.NetworkAttachmentType_Bridged: ('Bridged', adapter.bridgedInterface),
3075 ctx['global'].constants.NetworkAttachmentType_Internal: ('Internal', adapter.internalNetwork),
3076 ctx['global'].constants.NetworkAttachmentType_HostOnly: ('HostOnly', adapter.hostOnlyInterface),
3077 # @todo show details of the generic network attachment type
3078 ctx['global'].constants.NetworkAttachmentType_Generic: ('Generic', ''),
3079 }
3080 if not isinstance(adapter.attachmentType, int):
3081 t = str(adapter.attachmentType)
3082 else:
3083 t = adapter.attachmentType
3084 (resp, name) = nicAttachmentType[t]
3085 return (0, 'attachment:%s, name:%s' % (resp, name))
3086
3087 nicAttachmentType = {
3088 'Null': {
3089 'v': lambda: len(args) == 2,
3090 'p': lambda: 'do nothing',
3091 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Null},
3092 'NAT': {
3093 'v': lambda: len(args) == 2,
3094 'p': lambda: 'do nothing',
3095 'f': lambda: ctx['global'].constants.NetworkAttachmentType_NAT},
3096 'Bridged': {
3097 'v': lambda: len(args) == 3,
3098 'p': lambda: adapter.setattr('bridgedInterface', args[2]),
3099 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Bridged},
3100 'Internal': {
3101 'v': lambda: len(args) == 3,
3102 'p': lambda: adapter.setattr('internalNetwork', args[2]),
3103 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Internal},
3104 'HostOnly': {
3105 'v': lambda: len(args) == 2,
3106 'p': lambda: adapter.setattr('hostOnlyInterface', args[2]),
3107 'f': lambda: ctx['global'].constants.NetworkAttachmentType_HostOnly},
3108 # @todo implement setting the properties of a generic attachment
3109 'Generic': {
3110 'v': lambda: len(args) == 3,
3111 'p': lambda: 'do nothing',
3112 'f': lambda: ctx['global'].constants.NetworkAttachmentType_Generic}
3113 }
3114 if args[1] not in list(nicAttachmentType):
3115 print('%s not in acceptable values (%s)' % (args[1], list(nicAttachmentType.keys())))
3116 return (1, None)
3117 if not nicAttachmentType[args[1]]['v']():
3118 ## @todo r=andy Log this properly!
3119 return (1, None)
3120 nicAttachmentType[args[1]]['p']()
3121 adapter.attachmentType = nicAttachmentType[args[1]]['f']()
3122 return (0, None)
3123
3124def nicCmd(ctx, args):
3125 '''
3126 This command to manage network adapters
3127 usage: nic <vmname|uuid> <nicnum> <cmd> <cmd-args>
3128 where cmd : attachment, trace, linespeed, cable, enable, type
3129 '''
3130 # 'command name':{'runtime': is_callable_at_runtime, 'op': function_name}
3131 niccomand = {
3132 'attachment': nicAttachmentSubCmd,
3133 'trace': nicTraceSubCmd,
3134 'linespeed': nicLineSpeedSubCmd,
3135 'cable': nicCableSubCmd,
3136 'enable': nicEnableSubCmd,
3137 'type': nicTypeSubCmd
3138 }
3139 if len(args) < 2 \
3140 or args[1] == 'help' \
3141 or (len(args) > 2 and args[3] not in niccomand):
3142 if len(args) == 3 \
3143 and args[2] in niccomand:
3144 print(niccomand[args[2]].__doc__)
3145 else:
3146 print(nicCmd.__doc__)
3147 return 0
3148
3149 mach = ctx['argsToMach'](args)
3150 if not mach:
3151 return 1
3152
3153 platformProps = mach.platform.properties
3154 if len(args) < 3 \
3155 or int(args[2]) not in list(range(0, platformProps.getMaxNetworkAdapters(mach.platform.chipsetType))):
3156 print('please specify adapter num %d isn\'t in range [0-%d]'% (args[2], platformProps.getMaxNetworkAdapters(mach.platform.chipsetType)))
3157 return 1
3158 nicnum = int(args[2])
3159 cmdargs = args[3:]
3160 func = args[3]
3161 session = None
3162 session = ctx['global'].openMachineSession(mach, fPermitSharing=True)
3163 mach = session.machine
3164 adapter = mach.getNetworkAdapter(nicnum)
3165 (rc, report) = niccomand[func](ctx, mach, nicnum, adapter, cmdargs)
3166 if rc == 0:
3167 mach.saveSettings()
3168 if report is not None:
3169 print('%s nic %d %s: %s' % (mach.name, nicnum, args[3], report))
3170 session.unlockMachine()
3171 return 0
3172
3173
3174def promptCmd(ctx, args):
3175 if len(args) < 2:
3176 print("Current prompt: '%s'" % (ctx['prompt']))
3177 return 0
3178
3179 ctx['prompt'] = args[1]
3180 return 0
3181
3182def foreachCmd(ctx, args):
3183 if len(args) < 3:
3184 print("usage: foreach scope command, where scope is XPath-like expression //vms/vm[@CPUCount='2']")
3185 return 0
3186
3187 scope = args[1]
3188 cmd = args[2]
3189 elems = eval_xpath(ctx, scope)
3190 try:
3191 for e in elems:
3192 e.apply(cmd)
3193 except:
3194 print("Error executing")
3195 traceback.print_exc()
3196 return 0
3197
3198def foreachvmCmd(ctx, args):
3199 if len(args) < 2:
3200 print("foreachvm command <args>")
3201 return 0
3202 cmdargs = args[1:]
3203 cmdargs.insert(1, '')
3204 for mach in getMachines(ctx):
3205 cmdargs[1] = mach.id
3206 runCommandArgs(ctx, cmdargs)
3207 return 0
3208
3209def recordDemoCmd(ctx, args):
3210 if len(args) < 3:
3211 print("usage: recordDemo <vmname|uuid> <filename> [duration in s]")
3212 return 0
3213 mach = argsToMach(ctx, args)
3214 if not mach:
3215 return 0
3216 filename = args[2]
3217 dur = 10000
3218 if len(args) > 3:
3219 dur = float(args[3])
3220 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: recordDemo(ctx, console, filename, dur)])
3221 return 0
3222
3223def playbackDemoCmd(ctx, args):
3224 if len(args) < 3:
3225 print("usage: playbackDemo <vmname|uuid> <filename> [duration in s]")
3226 return 0
3227 mach = argsToMach(ctx, args)
3228 if not mach:
3229 return 0
3230 filename = args[2]
3231 dur = 10000
3232 if len(args) > 3:
3233 dur = float(args[3])
3234 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: playbackDemo(ctx, console, filename, dur)])
3235 return 0
3236
3237
3238def pciAddr(ctx, addr):
3239 strg = "%02x:%02x.%d" % (addr >> 8, (addr & 0xff) >> 3, addr & 7)
3240 return colPci(ctx, strg)
3241
3242def lspci(ctx, console):
3243 assigned = ctx['global'].getArray(console.machine, 'PCIDeviceAssignments')
3244 for assignment in assigned:
3245 if assignment.isPhysicalDevice:
3246 print("%s: assigned host device %s guest %s" % (colDev(ctx, assignment.name), pciAddr(ctx, assignment.hostAddress), pciAddr(ctx, assignment.guestAddress)))
3247
3248 atts = ctx['global'].getArray(console, 'attachedPCIDevices')
3249 for att in atts:
3250 if att.isPhysicalDevice:
3251 print("%s: physical, guest %s, host %s" % (colDev(ctx, att.name), pciAddr(ctx, att.guestAddress), pciAddr(ctx, att.hostAddress)))
3252 else:
3253 print("%s: virtual, guest %s" % (colDev(ctx, att.name), pciAddr(ctx, att.guestAddress)))
3254 return
3255
3256def parsePci(strg):
3257 pcire = re.compile(r'(?P<b>[0-9a-fA-F]+):(?P<d>[0-9a-fA-F]+)\.(?P<f>\d)')
3258 match = pcire.search(strg)
3259 if match is None:
3260 return -1
3261 pdict = match.groupdict()
3262 return ((int(pdict['b'], 16)) << 8) | ((int(pdict['d'], 16)) << 3) | int(pdict['f'])
3263
3264def lspciCmd(ctx, args):
3265 if len(args) < 2:
3266 print("usage: lspci vm")
3267 return 0
3268 mach = argsToMach(ctx, args)
3269 if not mach:
3270 return 0
3271 cmdExistingVm(ctx, mach, 'guestlambda', [lambda ctx, mach, console, args: lspci(ctx, console)])
3272 return 0
3273
3274def attachpciCmd(ctx, args):
3275 if len(args) < 3:
3276 print("usage: attachpci <vmname|uuid> <host pci address> <guest pci address>")
3277 return 0
3278 mach = argsToMach(ctx, args)
3279 if not mach:
3280 return 0
3281 hostaddr = parsePci(args[2])
3282 if hostaddr == -1:
3283 print("invalid host PCI %s, accepted format 01:02.3 for bus 1, device 2, function 3" % (args[2]))
3284 return 0
3285
3286 if len(args) > 3:
3287 guestaddr = parsePci(args[3])
3288 if guestaddr == -1:
3289 print("invalid guest PCI %s, accepted format 01:02.3 for bus 1, device 2, function 3" % (args[3]))
3290 return 0
3291 else:
3292 guestaddr = hostaddr
3293 cmdClosedVm(ctx, mach, lambda ctx, mach, a: mach.attachHostPCIDevice(hostaddr, guestaddr, True))
3294 return 0
3295
3296def detachpciCmd(ctx, args):
3297 if len(args) < 3:
3298 print("usage: detachpci <vmname|uuid> <host pci address>")
3299 return 0
3300 mach = argsToMach(ctx, args)
3301 if not mach:
3302 return 0
3303 hostaddr = parsePci(args[2])
3304 if hostaddr == -1:
3305 print("invalid host PCI %s, accepted format 01:02.3 for bus 1, device 2, function 3" % (args[2]))
3306 return 0
3307
3308 cmdClosedVm(ctx, mach, lambda ctx, mach, a: mach.detachHostPCIDevice(hostaddr))
3309 return 0
3310
3311def gotoCmd(ctx, args):
3312 if len(args) < 2:
3313 print("usage: goto line")
3314 return 0
3315
3316 line = int(args[1])
3317
3318 ctx['scriptLine'] = line
3319
3320 return 0
3321
3322aliases = {'s':'start',
3323 'i':'info',
3324 'l':'list',
3325 'h':'help',
3326 'a':'alias',
3327 'q':'quit', 'exit':'quit',
3328 'tg': 'typeGuest',
3329 'v':'verbose'}
3330
3331commands = {'help':['Prints help information', helpCmd, 0],
3332 'start':['Start virtual machine by name or uuid: start mytestvm headless', startCmd, 0],
3333 'createVm':['Create virtual machine: createVm myvmname x86 MacOS', createVmCmd, 0],
3334 'removeVm':['Remove virtual machine', removeVmCmd, 0],
3335 'pause':['Pause virtual machine', pauseCmd, 0],
3336 'resume':['Resume virtual machine', resumeCmd, 0],
3337 'save':['Save execution state of virtual machine', saveCmd, 0],
3338 'stats':['Stats for virtual machine', statsCmd, 0],
3339 'powerdown':['Power down virtual machine', powerdownCmd, 0],
3340 'powerbutton':['Effectively press power button', powerbuttonCmd, 0],
3341 'list':['Shows known virtual machines', listCmd, 0],
3342 'info':['Shows info on machine', infoCmd, 0],
3343 'ginfo':['Shows info on guest', ginfoCmd, 0],
3344 'gexec':['Executes program in the guest', gexecCmd, 0],
3345 'gcopy':['Copy file to the guest', gcopyCmd, 0],
3346 'gpipe':['Pipe between host and guest', gpipeCmd, 0],
3347 'alias':['Control aliases', aliasCmd, 0],
3348 'verbose':['Toggle verbosity', verboseCmd, 0],
3349 'setvar':['Set VM variable: setvar mytestvm firmwareSettings.ACPIEnabled True', setvarCmd, 0],
3350 'eval':['Evaluate arbitrary Python construction: eval \'for m in getMachines(ctx): print(m.name, "has", m.memorySize, "M")\'', evalCmd, 0],
3351 'quit':['Exits', quitCmd, 0],
3352 'host':['Show host information', hostCmd, 0],
3353 'guest':['Execute command for guest: guest mytestvm \'console.mouse.putMouseEvent(20, 20, 0, 0, 0)\'', guestCmd, 0],
3354 'monitorGuest':['Monitor what happens with the guest for some time: monitorGuest mytestvm 10', monitorGuestCmd, 0],
3355 'monitorGuestKbd':['Monitor guest keyboard for some time: monitorGuestKbd mytestvm 10', monitorGuestKbdCmd, 0],
3356 'monitorGuestMouse':['Monitor guest mouse for some time: monitorGuestMouse mytestvm 10', monitorGuestMouseCmd, 0],
3357 'monitorGuestMultiTouch':['Monitor guest touch screen for some time: monitorGuestMultiTouch mytestvm 10', monitorGuestMultiTouchCmd, 0],
3358 'monitorVBox':['Monitor what happens with VirtualBox for some time: monitorVBox 10', monitorVBoxCmd, 0],
3359 'portForward':['Setup permanent port forwarding for a VM, takes adapter number host port and guest port: portForward mytestvm 0 8080 80', portForwardCmd, 0],
3360 'showLog':['Show log file of the VM, : showLog mytestvm', showLogCmd, 0],
3361 'findLog':['Show entries matching pattern in log file of the VM, : findLog mytestvm PDM|CPUM', findLogCmd, 0],
3362 'findAssert':['Find assert in log file of the VM, : findAssert mytestvm', findAssertCmd, 0],
3363 'reloadExt':['Reload custom extensions: reloadExt', reloadExtCmd, 0],
3364 'runScript':['Run VBox script: runScript script.vbox', runScriptCmd, 0],
3365 'sleep':['Sleep for specified number of seconds: sleep 3.14159', sleepCmd, 0],
3366 'shell':['Execute external shell command: shell "ls /etc/rc*"', shellCmd, 0],
3367 'exportVm':['Export VM in OVF format: exportVm mytestvm /tmp/win.ovf', exportVMCmd, 0],
3368 'screenshot':['Take VM screenshot to a file: screenshot mytestvm /tmp/win.png 1024 768 0', screenshotCmd, 0],
3369 'teleport':['Teleport VM to another box (see openportal): teleport mytestvm anotherhost:8000 <passwd> <maxDowntime>', teleportCmd, 0],
3370 'typeGuest':['Type arbitrary text in guest: typeGuest Linux "^lls\\n&UP;&BKSP;ess /etc/hosts\\nq^c" 0.7', typeGuestCmd, 0],
3371 'openportal':['Open portal for teleportation of VM from another box (see teleport): openportal mytestvm 8000 <passwd>', openportalCmd, 0],
3372 'closeportal':['Close teleportation portal (see openportal, teleport): closeportal Win', closeportalCmd, 0],
3373 'getextra':['Get extra data, empty key lists all: getextra <vm|global> <key>', getExtraDataCmd, 0],
3374 'setextra':['Set extra data, empty value removes key: setextra <vm|global> <key> <value>', setExtraDataCmd, 0],
3375 'gueststats':['Print available guest stats (only Windows guests with additions so far): gueststats mytestvm', gueststatsCmd, 0],
3376 'plugcpu':['Add a CPU to a running VM: plugcpu mytestvm 1', plugcpuCmd, 0],
3377 'unplugcpu':['Remove a CPU from a running VM (additions required, Windows cannot unplug): unplugcpu Linux 1', unplugcpuCmd, 0],
3378 'createHdd': ['Create virtual HDD: createHdd 1000 /disk.vdi ', createHddCmd, 0],
3379 'removeHdd': ['Permanently remove virtual HDD: removeHdd /disk.vdi', removeHddCmd, 0],
3380 'registerHdd': ['Register HDD image with VirtualBox instance: registerHdd /disk.vdi', registerHddCmd, 0],
3381 'unregisterHdd': ['Unregister HDD image with VirtualBox instance: unregisterHdd /disk.vdi', unregisterHddCmd, 0],
3382 'attachHdd': ['Attach HDD to the VM: attachHdd mytestvm /disk.vdi "IDE Controller" 0:1', attachHddCmd, 0],
3383 'detachHdd': ['Detach HDD from the VM: detachHdd mytestvm /disk.vdi', detachHddCmd, 0],
3384 'registerIso': ['Register CD/DVD image with VirtualBox instance: registerIso /os.iso', registerIsoCmd, 0],
3385 'unregisterIso': ['Unregister CD/DVD image with VirtualBox instance: unregisterIso /os.iso', unregisterIsoCmd, 0],
3386 'removeIso': ['Permanently remove CD/DVD image: removeIso /os.iso', removeIsoCmd, 0],
3387 'attachIso': ['Attach CD/DVD to the VM: attachIso mytestvm /os.iso "IDE Controller" 0:1', attachIsoCmd, 0],
3388 'detachIso': ['Detach CD/DVD from the VM: detachIso mytestvm /os.iso', detachIsoCmd, 0],
3389 'mountIso': ['Mount CD/DVD to the running VM: mountIso mytestvm /os.iso "IDE Controller" 0:1', mountIsoCmd, 0],
3390 'unmountIso': ['Unmount CD/DVD from running VM: unmountIso mytestvm "IDE Controller" 0:1', unmountIsoCmd, 0],
3391 'attachCtr': ['Attach storage controller to the VM: attachCtr mytestvm Ctr0 IDE ICH6', attachCtrCmd, 0],
3392 'detachCtr': ['Detach HDD from the VM: detachCtr mytestvm Ctr0', detachCtrCmd, 0],
3393 'attachUsb': ['Attach USB device to the VM (use listUsb to show available devices): attachUsb mytestvm uuid', attachUsbCmd, 0],
3394 'detachUsb': ['Detach USB device from the VM: detachUsb mytestvm uuid', detachUsbCmd, 0],
3395 'listMedia': ['List media known to this VBox instance', listMediaCmd, 0],
3396 'listUsb': ['List known USB devices', listUsbCmd, 0],
3397 'shareFolder': ['Make host\'s folder visible to guest: shareFolder mytestvm /share share writable', shareFolderCmd, 0],
3398 'unshareFolder': ['Remove folder sharing', unshareFolderCmd, 0],
3399 'gui': ['Start GUI frontend', guiCmd, 0],
3400 'colors':['Toggle colors', colorsCmd, 0],
3401 'snapshot':['VM snapshot manipulation, snapshot help for more info', snapshotCmd, 0],
3402 'nat':['NAT (network address translation engine) manipulation, nat help for more info', natCmd, 0],
3403 'nic' : ['Network adapter management', nicCmd, 0],
3404 'prompt' : ['Control shell prompt', promptCmd, 0],
3405 'foreachvm' : ['Perform command for each VM', foreachvmCmd, 0],
3406 'foreach' : ['Generic "for each" construction, using XPath-like notation: foreach //vms/vm[@OSTypeId=\'MacOS\'] "print(obj.name)"', foreachCmd, 0],
3407 'recordDemo':['Record demo: recordDemo mytestvm file.dmo 10', recordDemoCmd, 0],
3408 'playbackDemo':['Playback demo: playbackDemo mytestvm file.dmo 10', playbackDemoCmd, 0],
3409 'lspci': ['List PCI devices attached to the VM: lspci mytestvm', lspciCmd, 0],
3410 'attachpci': ['Attach host PCI device to the VM: attachpci mytestvm 01:00.0', attachpciCmd, 0],
3411 'detachpci': ['Detach host PCI device from the VM: detachpci mytestvm 01:00.0', detachpciCmd, 0],
3412 'goto': ['Go to line in script (script-only)', gotoCmd, 0]
3413 }
3414
3415def runCommandArgs(ctx, args):
3416 c = args[0]
3417 if aliases.get(c, None):
3418 c = aliases[c]
3419 cmd_internal = commands.get(c, None)
3420 if not cmd_internal:
3421 print("Unknown command: '%s', type 'help' for list of known commands" % (c))
3422 return 0
3423 if ctx['remote'] and ctx['vb'] is None:
3424 if c not in ['connect', 'reconnect', 'help', 'quit']:
3425 print("First connect to remote server with %s command." % (colored('connect', 'blue')))
3426 return 0
3427 return cmd_internal[1](ctx, args)
3428
3429
3430def runCommand(ctx, cmd):
3431 if not cmd: return 0
3432 args = split_no_quotes(cmd)
3433 if len(args) == 0: return 0
3434 return runCommandArgs(ctx, args)
3435
3436#
3437# To write your own custom commands to vboxshell, create
3438# file ~/.VirtualBox/shellext.py with content like
3439#
3440# def runTestCmd(ctx, args):
3441# print("Testy test", ctx['vb'])
3442# return 0
3443#
3444# commands = {
3445# 'test': ['Test help', runTestCmd]
3446# }
3447# and issue reloadExt shell command.
3448# This file also will be read automatically on startup or 'reloadExt'.
3449#
3450# Also one can put shell extensions into ~/.VirtualBox/shexts and
3451# they will also be picked up, so this way one can exchange
3452# shell extensions easily.
3453def addExtsFromFile(_ctx, cmds, filename):
3454 if not os.path.isfile(filename):
3455 return
3456 extDict = {}
3457 try:
3458 with open(filename, encoding='utf-8') as file:
3459 file_buf = file.read()
3460 exec(compile(file_buf, filename, 'exec'), extDict, extDict) # pylint: disable=exec-used
3461 for (key, value) in list(extDict['commands'].items()):
3462 if g_fVerbose:
3463 print("customize: adding \"%s\" - %s" % (key, value[0]))
3464 cmds[key] = [value[0], value[1], filename]
3465 except:
3466 print("Error loading user extensions from %s" % (filename))
3467 traceback.print_exc()
3468
3469
3470def checkUserExtensions(ctx, cmds, folder):
3471 folder = str(folder)
3472 name = os.path.join(folder, "shellext.py")
3473 addExtsFromFile(ctx, cmds, name)
3474 # also check 'exts' directory for all files
3475 shextdir = os.path.join(folder, "shexts")
3476 if not os.path.isdir(shextdir):
3477 return
3478 exts = os.listdir(shextdir)
3479 for e in exts:
3480 # not editor temporary files, please.
3481 if e.endswith('.py'):
3482 addExtsFromFile(ctx, cmds, os.path.join(shextdir, e))
3483
3484def getHomeFolder(ctx):
3485 if ctx['remote'] or ctx['vb'] is None:
3486 if 'VBOX_USER_HOME' in os.environ:
3487 return os.path.join(os.environ['VBOX_USER_HOME'])
3488 return os.path.join(os.path.expanduser("~"), ".VirtualBox")
3489
3490 return ctx['vb'].homeFolder
3491
3492def interpret(ctx):
3493 if ctx['remote']:
3494 commands['connect'] = ["Connect to remote VBox instance: connect http://server:18083 user password", connectCmd, 0]
3495 commands['disconnect'] = ["Disconnect from remote VBox instance", disconnectCmd, 0]
3496 commands['reconnect'] = ["Reconnect to remote VBox instance", reconnectCmd, 0]
3497 ctx['wsinfo'] = ["http://localhost:18083", "", ""]
3498
3499 vbox = ctx['vb']
3500 if vbox is not None:
3501 try:
3502 print("Running VirtualBox version %s" % (vbox.version))
3503 except Exception as e:
3504 printErr(ctx, e)
3505 if g_fVerbose:
3506 traceback.print_exc()
3507 ctx['perf'] = None # ctx['global'].getPerfCollector(vbox)
3508 else:
3509 ctx['perf'] = None
3510
3511 home = getHomeFolder(ctx)
3512 checkUserExtensions(ctx, commands, home)
3513 if platform.system() in ['Windows', 'Microsoft']:
3514 global g_fHasColors
3515 g_fHasColors = False
3516 hist_file = os.path.join(home, ".vboxshellhistory")
3517 autoCompletion(commands, ctx)
3518
3519 if g_fHasReadline and os.path.exists(hist_file):
3520 readline.read_history_file(hist_file)
3521
3522 # to allow to print actual host information, we collect info for
3523 # last 150 secs maximum, (sample every 10 secs and keep up to 15 samples)
3524 if ctx['perf']:
3525 try:
3526 ctx['perf'].setup(['*'], [vbox.host], 10, 15)
3527 except:
3528 pass
3529 cmds = []
3530
3531 if g_sCmd is not None:
3532 cmds = g_sCmd.split(';')
3533 itCmd = iter(cmds)
3534
3535 while True:
3536 try:
3537 if g_fBatchMode:
3538 cmd = 'runScript %s'% (g_sScriptFile)
3539 elif g_sCmd is not None:
3540 cmd = next(itCmd)
3541 else:
3542 if sys.version_info[0] <= 2:
3543 cmd = raw_input(ctx['prompt']) # pylint: disable=undefined-variable
3544 else:
3545 cmd = input(ctx['prompt'])
3546 done = runCommand(ctx, cmd)
3547 if done != 0:
3548 break
3549 if g_fBatchMode:
3550 break
3551 except KeyboardInterrupt:
3552 print('====== You can type quit or q to leave')
3553 except StopIteration:
3554 break
3555 except EOFError:
3556 break
3557 except Exception as e:
3558 printErr(ctx, e)
3559 if g_fVerbose:
3560 traceback.print_exc()
3561 ctx['global'].waitForEvents(0)
3562 try:
3563 # There is no need to disable metric collection. This is just an example.
3564 if ctx['perf']:
3565 ctx['perf'].disable(['*'], [vbox.host])
3566 except:
3567 pass
3568 if g_fHasReadline:
3569 readline.write_history_file(hist_file)
3570
3571def runCommandCb(ctx, cmd, args):
3572 args.insert(0, cmd)
3573 return runCommandArgs(ctx, args)
3574
3575def runGuestCommandCb(ctx, uuid, guestLambda, args):
3576 mach = machById(ctx, uuid)
3577 if not mach:
3578 return 0
3579 args.insert(0, guestLambda)
3580 cmdExistingVm(ctx, mach, 'guestlambda', args)
3581 return 0
3582
3583def main(_argv):
3584
3585 #
3586 # Parse command line arguments.
3587 #
3588 parse = OptionParser()
3589 parse.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False, help = "switch on verbose")
3590 parse.add_option("-a", "--autopath", dest="autopath", action="store_true", default=False, help = "switch on autopath")
3591 parse.add_option("-w", "--webservice", dest="style", action="store_const", const="WEBSERVICE", help = "connect to webservice")
3592 parse.add_option("-b", "--batch", dest="batch_file", help = "script file to execute")
3593 parse.add_option("-c", dest="command_line", help = "command sequence to execute")
3594 parse.add_option("-o", dest="opt_line", help = "option line")
3595 global g_fVerbose, g_sScriptFile, g_fBatchMode, g_fHasColors, g_fHasReadline, g_sCmd
3596 (options, _args) = parse.parse_args()
3597 g_fVerbose = options.verbose
3598 style = options.style
3599 if options.batch_file is not None:
3600 g_fBatchMode = True
3601 g_fHasColors = False
3602 g_fHasReadline = False
3603 g_sScriptFile = options.batch_file
3604 if options.command_line is not None:
3605 g_fHasColors = False
3606 g_fHasReadline = False
3607 g_sCmd = options.command_line
3608
3609 params = None
3610 if options.opt_line is not None:
3611 params = {}
3612 strparams = options.opt_line
3613 strparamlist = strparams.split(',')
3614 for strparam in strparamlist:
3615 (key, value) = strparam.split('=')
3616 params[key] = value
3617
3618 if options.autopath:
3619 asLocations = [ os.getcwd(), ]
3620 try: sScriptDir = os.path.dirname(os.path.abspath(__file__))
3621 except: pass # In case __file__ isn't there.
3622 else:
3623 if platform.system() in [ 'SunOS', ]:
3624 asLocations.append(os.path.join(sScriptDir, 'amd64'))
3625 asLocations.append(sScriptDir)
3626
3627
3628 sPath = os.environ.get("VBOX_PROGRAM_PATH")
3629 if sPath is None:
3630 for sCurLoc in asLocations:
3631 if os.path.isfile(os.path.join(sCurLoc, "VirtualBox")) \
3632 or os.path.isfile(os.path.join(sCurLoc, "VirtualBox.exe")):
3633 print("Autodetected VBOX_PROGRAM_PATH as", sCurLoc)
3634 os.environ["VBOX_PROGRAM_PATH"] = sCurLoc
3635 sPath = sCurLoc
3636 break
3637 if sPath:
3638 sys.path.append(os.path.join(sPath, "sdk", "installer"))
3639
3640 sPath = os.environ.get("VBOX_SDK_PATH")
3641 if sPath is None:
3642 for sCurLoc in asLocations:
3643 if os.path.isfile(os.path.join(sCurLoc, "sdk", "bindings", "VirtualBox.xidl")):
3644 sCurLoc = os.path.join(sCurLoc, "sdk")
3645 print("Autodetected VBOX_SDK_PATH as", sCurLoc)
3646 os.environ["VBOX_SDK_PATH"] = sCurLoc
3647 sPath = sCurLoc
3648 break
3649 if sPath:
3650 sCurLoc = sPath
3651 sTmp = os.path.join(sCurLoc, 'bindings', 'xpcom', 'python')
3652 if os.path.isdir(sTmp):
3653 sys.path.append(sTmp)
3654 del sTmp
3655 del sPath, asLocations
3656
3657 # Deprecation warning for older Python stuff (< Python 3.x).
3658 if sys.version_info.major < 3:
3659 print("\nWarning: Running VirtualBox with Python %d.%d is marked as being deprecated.\n" \
3660 "Please upgrade your Python installation to avoid breakage.\n" \
3661 % (sys.version_info.major, sys.version_info.minor))
3662
3663 #
3664 # Set up the shell interpreter context and start working.
3665 #
3666 from vboxapi import VirtualBoxManager
3667 oVBoxMgr = VirtualBoxManager(style, params)
3668 ctx = {
3669 'global': oVBoxMgr,
3670 'vb': oVBoxMgr.getVirtualBox(),
3671 'const': oVBoxMgr.constants,
3672 'remote': oVBoxMgr.remote,
3673 'type': oVBoxMgr.type,
3674 'run': lambda cmd, args: runCommandCb(ctx, cmd, args),
3675 'guestlambda': lambda uuid, guestLambda, args: runGuestCommandCb(ctx, uuid, guestLambda, args),
3676 'machById': lambda uuid: machById(ctx, uuid),
3677 'argsToMach': lambda args: argsToMach(ctx, args),
3678 'progressBar': lambda p: progressBar(ctx, p),
3679 'typeInGuest': typeInGuest,
3680 '_machlist': None,
3681 'prompt': g_sPrompt,
3682 'scriptLine': 0,
3683 'interrupt': False,
3684 }
3685 interpret(ctx)
3686
3687 #
3688 # Release the interfaces references in ctx before cleaning up.
3689 #
3690 for sKey in list(ctx.keys()):
3691 del ctx[sKey]
3692 ctx = None
3693 gc.collect()
3694
3695 oVBoxMgr.deinit()
3696 del oVBoxMgr
3697
3698if __name__ == '__main__':
3699 main(sys.argv)
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use