VirtualBox

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

Last change on this file since 102890 was 102890, checked in by vboxsync, 12 months ago

Frontends/VBoxShell: Use a shell wrapper code instead of a hardcoded Python shebang to make this frontend more compatible with newer distros. See comments for details.

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

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