VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testdriver/vboxinstaller.py@ 95890

最後變更 在這個檔案從95890是 95775,由 vboxsync 提交於 3 年 前

Validation Kit/vboxinstaller.py: Resolved a @todo: Use the scratch dir for the MSI log. ​bugref:8691

  • 屬性 svn:eol-style 設為 LF
  • 屬性 svn:executable 設為 *
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 48.8 KB
 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4"""
5VirtualBox Installer Wrapper Driver.
6
7This installs VirtualBox, starts a sub driver which does the real testing,
8and then uninstall VirtualBox afterwards. This reduces the complexity of the
9other VBox test drivers.
10"""
11
12__copyright__ = \
13"""
14Copyright (C) 2010-2022 Oracle Corporation
15
16This file is part of VirtualBox Open Source Edition (OSE), as
17available from http://www.alldomusa.eu.org. This file is free software;
18you can redistribute it and/or modify it under the terms of the GNU
19General Public License (GPL) as published by the Free Software
20Foundation, in version 2 as it comes in the "COPYING" file of the
21VirtualBox OSE distribution. VirtualBox OSE is distributed in the
22hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
23
24The contents of this file may alternatively be used under the terms
25of the Common Development and Distribution License Version 1.0
26(CDDL) only, as it comes in the "COPYING.CDDL" file of the
27VirtualBox OSE distribution, in which case the provisions of the
28CDDL are applicable instead of those of the GPL.
29
30You may elect to license modified versions of this file under the
31terms and conditions of either the GPL or the CDDL or both.
32"""
33__version__ = "$Revision: 95775 $"
34
35
36# Standard Python imports.
37import os
38import sys
39import re
40import socket
41import tempfile
42import time
43
44# Only the main script needs to modify the path.
45try: __file__
46except: __file__ = sys.argv[0];
47g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
48sys.path.append(g_ksValidationKitDir);
49
50# Validation Kit imports.
51from common import utils, webutils;
52from common.constants import rtexitcode;
53from testdriver import reporter;
54from testdriver.base import TestDriverBase;
55
56
57
58class VBoxInstallerTestDriver(TestDriverBase):
59 """
60 Implementation of a top level test driver.
61 """
62
63
64 ## State file indicating that we've skipped installation.
65 ksVar_Skipped = 'vboxinstaller-skipped';
66
67
68 def __init__(self):
69 TestDriverBase.__init__(self);
70 self._asSubDriver = []; # The sub driver and it's arguments.
71 self._asBuildUrls = []; # The URLs passed us on the command line.
72 self._asBuildFiles = []; # The downloaded file names.
73 self._fUnpackedBuildFiles = False;
74 self._fAutoInstallPuelExtPack = True;
75 self._fKernelDrivers = True;
76 self._fWinForcedInstallTimestampCA = True;
77
78 #
79 # Base method we override
80 #
81
82 def showUsage(self):
83 rc = TestDriverBase.showUsage(self);
84 # 0 1 2 3 4 5 6 7 8
85 # 012345678901234567890123456789012345678901234567890123456789012345678901234567890
86 reporter.log('');
87 reporter.log('vboxinstaller Options:');
88 reporter.log(' --vbox-build <url[,url2[,...]]>');
89 reporter.log(' Comma separated list of URL to file to download and install or/and');
90 reporter.log(' unpack. URLs without a schema are assumed to be files on the');
91 reporter.log(' build share and will be copied off it.');
92 reporter.log(' --no-puel-extpack');
93 reporter.log(' Indicates that the PUEL extension pack should not be installed if found.');
94 reporter.log(' The default is to install it when found in the vbox-build.');
95 reporter.log(' --no-kernel-drivers');
96 reporter.log(' Indicates that the kernel drivers should not be installed on platforms');
97 reporter.log(' where this is optional. The default is to install them.');
98 reporter.log(' --forced-win-install-timestamp-ca, --no-forced-win-install-timestamp-ca');
99 reporter.log(' Whether to force installation of the legacy Windows timestamp CA.');
100 reporter.log(' If not forced, it will only installed on the hosts that needs it.');
101 reporter.log(' Default: --no-forced-win-install-timestamp-ca');
102 reporter.log(' --');
103 reporter.log(' Indicates the end of our parameters and the start of the sub');
104 reporter.log(' testdriver and its arguments.');
105 return rc;
106
107 def parseOption(self, asArgs, iArg):
108 """
109 Parse our arguments.
110 """
111 if asArgs[iArg] == '--':
112 # End of our parameters and start of the sub driver invocation.
113 iArg = self.requireMoreArgs(1, asArgs, iArg);
114 assert not self._asSubDriver;
115 self._asSubDriver = asArgs[iArg:];
116 self._asSubDriver[0] = self._asSubDriver[0].replace('/', os.path.sep);
117 iArg = len(asArgs) - 1;
118 elif asArgs[iArg] == '--vbox-build':
119 # List of files to copy/download and install.
120 iArg = self.requireMoreArgs(1, asArgs, iArg);
121 self._asBuildUrls = asArgs[iArg].split(',');
122 elif asArgs[iArg] == '--no-puel-extpack':
123 self._fAutoInstallPuelExtPack = False;
124 elif asArgs[iArg] == '--puel-extpack':
125 self._fAutoInstallPuelExtPack = True;
126 elif asArgs[iArg] == '--no-kernel-drivers':
127 self._fKernelDrivers = False;
128 elif asArgs[iArg] == '--kernel-drivers':
129 self._fKernelDrivers = True;
130 elif asArgs[iArg] == '--no-forced-win-install-timestamp-ca':
131 self._fWinForcedInstallTimestampCA = False;
132 elif asArgs[iArg] == '--forced-win-install-timestamp-ca':
133 self._fWinForcedInstallTimestampCA = True;
134 else:
135 return TestDriverBase.parseOption(self, asArgs, iArg);
136 return iArg + 1;
137
138 def completeOptions(self):
139 #
140 # Check that we've got what we need.
141 #
142 if not self._asBuildUrls:
143 reporter.error('No build files specified ("--vbox-build file1[,file2[...]]")');
144 return False;
145 if not self._asSubDriver:
146 reporter.error('No sub testdriver specified. (" -- test/stuff/tdStuff1.py args")');
147 return False;
148
149 #
150 # Construct _asBuildFiles as an array parallel to _asBuildUrls.
151 #
152 for sUrl in self._asBuildUrls:
153 sDstFile = os.path.join(self.sScratchPath, webutils.getFilename(sUrl));
154 self._asBuildFiles.append(sDstFile);
155
156 return TestDriverBase.completeOptions(self);
157
158 def actionExtract(self):
159 reporter.error('vboxinstall does not support extracting resources, you have to do that using the sub testdriver.');
160 return False;
161
162 def actionCleanupBefore(self):
163 """
164 Kills all VBox process we see.
165
166 This is only supposed to execute on a testbox so we don't need to go
167 all complicated wrt other users.
168 """
169 return self._killAllVBoxProcesses();
170
171 def actionConfig(self):
172 """
173 Install VBox and pass on the configure request to the sub testdriver.
174 """
175 fRc = self._installVBox();
176 if fRc is None:
177 self._persistentVarSet(self.ksVar_Skipped, 'true');
178 self.fBadTestbox = True;
179 else:
180 self._persistentVarUnset(self.ksVar_Skipped);
181
182 ## @todo vbox.py still has bugs preventing us from invoking it seperately with each action.
183 if fRc is True and 'execute' not in self.asActions and 'all' not in self.asActions:
184 fRc = self._executeSubDriver([ 'verify', ]);
185 if fRc is True and 'execute' not in self.asActions and 'all' not in self.asActions:
186 fRc = self._executeSubDriver([ 'config', ], fPreloadASan = True);
187 return fRc;
188
189 def actionExecute(self):
190 """
191 Execute the sub testdriver.
192 """
193 return self._executeSubDriver(self.asActions, fPreloadASan = True);
194
195 def actionCleanupAfter(self):
196 """
197 Forward this to the sub testdriver, then uninstall VBox.
198 """
199 fRc = True;
200 if 'execute' not in self.asActions and 'all' not in self.asActions:
201 fRc = self._executeSubDriver([ 'cleanup-after', ], fMaySkip = False);
202
203 if not self._killAllVBoxProcesses():
204 fRc = False;
205
206 if not self._uninstallVBox(self._persistentVarExists(self.ksVar_Skipped)):
207 fRc = False;
208
209 if utils.getHostOs() == 'darwin':
210 self._darwinUnmountDmg(fIgnoreError = True); # paranoia
211
212 if not TestDriverBase.actionCleanupAfter(self):
213 fRc = False;
214
215 return fRc;
216
217
218 def actionAbort(self):
219 """
220 Forward this to the sub testdriver first, then wipe all VBox like
221 processes, and finally do the pid file processing (again).
222 """
223 fRc1 = self._executeSubDriver([ 'abort', ], fMaySkip = False, fPreloadASan = True);
224 fRc2 = self._killAllVBoxProcesses();
225 fRc3 = TestDriverBase.actionAbort(self);
226 return fRc1 and fRc2 and fRc3;
227
228
229 #
230 # Persistent variables.
231 #
232 ## @todo integrate into the base driver. Persistent accross scratch wipes?
233
234 def __persistentVarCalcName(self, sVar):
235 """Returns the (full) filename for the given persistent variable."""
236 assert re.match(r'^[a-zA-Z0-9_-]*$', sVar) is not None;
237 return os.path.join(self.sScratchPath, 'persistent-%s.var' % (sVar,));
238
239 def _persistentVarSet(self, sVar, sValue = ''):
240 """
241 Sets a persistent variable.
242
243 Returns True on success, False + reporter.error on failure.
244
245 May raise exception if the variable name is invalid or something
246 unexpected happens.
247 """
248 sFull = self.__persistentVarCalcName(sVar);
249 try:
250 with open(sFull, 'w') as oFile:
251 if sValue:
252 oFile.write(sValue.encode('utf-8'));
253 except:
254 reporter.errorXcpt('Error creating "%s"' % (sFull,));
255 return False;
256 return True;
257
258 def _persistentVarUnset(self, sVar):
259 """
260 Unsets a persistent variable.
261
262 Returns True on success, False + reporter.error on failure.
263
264 May raise exception if the variable name is invalid or something
265 unexpected happens.
266 """
267 sFull = self.__persistentVarCalcName(sVar);
268 if os.path.exists(sFull):
269 try:
270 os.unlink(sFull);
271 except:
272 reporter.errorXcpt('Error unlinking "%s"' % (sFull,));
273 return False;
274 return True;
275
276 def _persistentVarExists(self, sVar):
277 """
278 Checks if a persistent variable exists.
279
280 Returns true/false.
281
282 May raise exception if the variable name is invalid or something
283 unexpected happens.
284 """
285 return os.path.exists(self.__persistentVarCalcName(sVar));
286
287 def _persistentVarGet(self, sVar):
288 """
289 Gets the value of a persistent variable.
290
291 Returns variable value on success.
292 Returns None if the variable doesn't exist or if an
293 error (reported) occured.
294
295 May raise exception if the variable name is invalid or something
296 unexpected happens.
297 """
298 sFull = self.__persistentVarCalcName(sVar);
299 if not os.path.exists(sFull):
300 return None;
301 try:
302 with open(sFull, 'r') as oFile:
303 sValue = oFile.read().decode('utf-8');
304 except:
305 reporter.errorXcpt('Error creating "%s"' % (sFull,));
306 return None;
307 return sValue;
308
309
310 #
311 # Helpers.
312 #
313
314 def _killAllVBoxProcesses(self):
315 """
316 Kills all virtual box related processes we find in the system.
317 """
318 sHostOs = utils.getHostOs();
319 asDebuggers = [ 'cdb', 'windbg', ] if sHostOs == 'windows' else [ 'gdb', 'gdb-i386-apple-darwin', 'lldb' ];
320
321 for iIteration in range(22):
322 # Gather processes to kill.
323 aoTodo = [];
324 aoDebuggers = [];
325 for oProcess in utils.processListAll():
326 sBase = oProcess.getBaseImageNameNoExeSuff();
327 if sBase is None:
328 continue;
329 sBase = sBase.lower();
330 if sBase in [ 'vboxsvc', 'vboxsds', 'virtualbox', 'virtualboxvm', 'vboxheadless', 'vboxmanage', 'vboxsdl',
331 'vboxwebsrv', 'vboxautostart', 'vboxballoonctrl', 'vboxbfe', 'vboxextpackhelperapp', 'vboxnetdhcp',
332 'vboxnetnat', 'vboxnetadpctl', 'vboxtestogl', 'vboxtunctl', 'vboxvmmpreload', 'vboxxpcomipcd', ]:
333 aoTodo.append(oProcess);
334 if sBase.startswith('virtualbox-') and sBase.endswith('-multiarch.exe'):
335 aoTodo.append(oProcess);
336 if sBase in asDebuggers:
337 aoDebuggers.append(oProcess);
338 if iIteration in [0, 21]:
339 reporter.log('Warning: debugger running: %s (%s %s)' % (oProcess.iPid, sBase, oProcess.asArgs));
340 if not aoTodo:
341 return True;
342
343 # Are any of the debugger processes hooked up to a VBox process?
344 if sHostOs == 'windows':
345 # On demand debugging windows: windbg -p <decimal-pid> -e <decimal-event> -g
346 for oDebugger in aoDebuggers:
347 for oProcess in aoTodo:
348 # The whole command line is asArgs[0] here. Fix if that changes.
349 if oDebugger.asArgs and oDebugger.asArgs[0].find('-p %s ' % (oProcess.iPid,)) >= 0:
350 aoTodo.append(oDebugger);
351 break;
352 else:
353 for oDebugger in aoDebuggers:
354 for oProcess in aoTodo:
355 # Simplistic approach: Just check for argument equaling our pid.
356 if oDebugger.asArgs and ('%s' % oProcess.iPid) in oDebugger.asArgs:
357 aoTodo.append(oDebugger);
358 break;
359
360 # Kill.
361 for oProcess in aoTodo:
362 reporter.log('Loop #%d - Killing %s (%s, uid=%s)'
363 % ( iIteration, oProcess.iPid, oProcess.sImage if oProcess.sName is None else oProcess.sName,
364 oProcess.iUid, ));
365 if not utils.processKill(oProcess.iPid) \
366 and sHostOs != 'windows' \
367 and utils.processExists(oProcess.iPid):
368 # Many of the vbox processes are initially set-uid-to-root and associated debuggers are running
369 # via sudo, so we might not be able to kill them unless we sudo and use /bin/kill.
370 try: utils.sudoProcessCall(['/bin/kill', '-9', '%s' % (oProcess.iPid,)]);
371 except: reporter.logXcpt();
372
373 # Check if they're all dead like they should be.
374 time.sleep(0.1);
375 for oProcess in aoTodo:
376 if utils.processExists(oProcess.iPid):
377 time.sleep(2);
378 break;
379
380 return False;
381
382 def _executeSync(self, asArgs, fMaySkip = False):
383 """
384 Executes a child process synchronously.
385
386 Returns True if the process executed successfully and returned 0.
387 Returns None if fMaySkip is true and the child exits with RTEXITCODE_SKIPPED.
388 Returns False for all other cases.
389 """
390 reporter.log('Executing: %s' % (asArgs, ));
391 reporter.flushall();
392 try:
393 iRc = utils.processCall(asArgs, shell = False, close_fds = False);
394 except:
395 reporter.errorXcpt();
396 return False;
397 reporter.log('Exit code: %s (%s)' % (iRc, asArgs));
398 if fMaySkip and iRc == rtexitcode.RTEXITCODE_SKIPPED:
399 return None;
400 return iRc == 0;
401
402 def _sudoExecuteSync(self, asArgs):
403 """
404 Executes a sudo child process synchronously.
405 Returns a tuple [True, 0] if the process executed successfully
406 and returned 0, otherwise [False, rc] is returned.
407 """
408 reporter.log('Executing [sudo]: %s' % (asArgs, ));
409 reporter.flushall();
410 iRc = 0;
411 try:
412 iRc = utils.sudoProcessCall(asArgs, shell = False, close_fds = False);
413 except:
414 reporter.errorXcpt();
415 return (False, 0);
416 reporter.log('Exit code [sudo]: %s (%s)' % (iRc, asArgs));
417 return (iRc == 0, iRc);
418
419 def _findASanLibsForASanBuild(self):
420 """
421 Returns a list of (address) santizier related libraries to preload
422 when launching the sub driver.
423 Returns empty list for non-asan builds or on platforms where this isn't needed.
424 """
425 # Note! We include libasan.so.X in the VBoxAll tarball for asan builds, so we
426 # can use its presence both to detect an 'asan' build and to return it.
427 # Only the libasan.so.X library needs preloading at present.
428 if self.sHost in ('linux',):
429 sLibASan = self._findFile(r'libasan\.so\..*');
430 if sLibASan:
431 return [sLibASan,];
432 return [];
433
434 def _executeSubDriver(self, asActions, fMaySkip = True, fPreloadASan = True):
435 """
436 Execute the sub testdriver with the specified action.
437 """
438 asArgs = list(self._asSubDriver)
439 asArgs.append('--no-wipe-clean');
440 asArgs.extend(asActions);
441
442 asASanLibs = [];
443 if fPreloadASan:
444 asASanLibs = self._findASanLibsForASanBuild();
445 if asASanLibs:
446 os.environ['LD_PRELOAD'] = ':'.join(asASanLibs);
447 os.environ['LSAN_OPTIONS'] = 'detect_leaks=0'; # We don't want python leaks. vbox.py disables this.
448
449 # Because of https://github.com/google/sanitizers/issues/856 we must try use setarch to disable
450 # address space randomization.
451
452 reporter.log('LD_PRELOAD...')
453 if utils.getHostArch() == 'amd64':
454 sSetArch = utils.whichProgram('setarch');
455 reporter.log('sSetArch=%s' % (sSetArch,));
456 if sSetArch:
457 asArgs = [ sSetArch, 'x86_64', '-R', sys.executable ] + asArgs;
458 reporter.log('asArgs=%s' % (asArgs,));
459
460 rc = self._executeSync(asArgs, fMaySkip = fMaySkip);
461
462 del os.environ['LSAN_OPTIONS'];
463 del os.environ['LD_PRELOAD'];
464 return rc;
465
466 return self._executeSync(asArgs, fMaySkip = fMaySkip);
467
468 def _maybeUnpackArchive(self, sMaybeArchive, fNonFatal = False):
469 """
470 Attempts to unpack the given build file.
471 Updates _asBuildFiles.
472 Returns True/False. No exceptions.
473 """
474 def unpackFilter(sMember):
475 # type: (string) -> bool
476 """ Skips debug info. """
477 sLower = sMember.lower();
478 if sLower.endswith('.pdb'):
479 return False;
480 return True;
481
482 asMembers = utils.unpackFile(sMaybeArchive, self.sScratchPath, reporter.log,
483 reporter.log if fNonFatal else reporter.error,
484 fnFilter = unpackFilter);
485 if asMembers is None:
486 return False;
487 self._asBuildFiles.extend(asMembers);
488 return True;
489
490
491 def _installVBox(self):
492 """
493 Download / copy the build files into the scratch area and install them.
494 """
495 reporter.testStart('Installing VirtualBox');
496 reporter.log('CWD=%s' % (os.getcwd(),)); # curious
497
498 #
499 # Download the build files.
500 #
501 for i, sBuildUrl in enumerate(self._asBuildUrls):
502 if webutils.downloadFile(sBuildUrl, self._asBuildFiles[i], self.sBuildPath, reporter.log, reporter.log) is not True:
503 reporter.testDone(fSkipped = True);
504 return None; # Failed to get binaries, probably deleted. Skip the test run.
505
506 #
507 # Unpack anything we know what is and append it to the build files
508 # list. This allows us to use VBoxAll*.tar.gz files.
509 #
510 for sFile in list(self._asBuildFiles): # Note! We copy the list as _maybeUnpackArchive updates it.
511 if self._maybeUnpackArchive(sFile, fNonFatal = True) is not True:
512 reporter.testDone(fSkipped = True);
513 return None; # Failed to unpack. Probably local error, like busy
514 # DLLs on windows, no reason for failing the build.
515 self._fUnpackedBuildFiles = True;
516
517 #
518 # Go to system specific installation code.
519 #
520 sHost = utils.getHostOs()
521 if sHost == 'darwin': fRc = self._installVBoxOnDarwin();
522 elif sHost == 'linux': fRc = self._installVBoxOnLinux();
523 elif sHost == 'solaris': fRc = self._installVBoxOnSolaris();
524 elif sHost == 'win': fRc = self._installVBoxOnWindows();
525 else:
526 reporter.error('Unsupported host "%s".' % (sHost,));
527 if fRc is False:
528 reporter.testFailure('Installation error.');
529 elif fRc is not True:
530 reporter.log('Seems installation was skipped. Old version lurking behind? Not the fault of this build/test run!');
531
532 #
533 # Install the extension pack.
534 #
535 if fRc is True and self._fAutoInstallPuelExtPack:
536 fRc = self._installExtPack();
537 if fRc is False:
538 reporter.testFailure('Extension pack installation error.');
539
540 # Some debugging...
541 try:
542 cMbFreeSpace = utils.getDiskUsage(self.sScratchPath);
543 reporter.log('Disk usage after VBox install: %d MB available at %s' % (cMbFreeSpace, self.sScratchPath,));
544 except:
545 reporter.logXcpt('Unable to get disk free space. Ignored. Continuing.');
546
547 reporter.testDone(fRc is None);
548 return fRc;
549
550 def _uninstallVBox(self, fIgnoreError = False):
551 """
552 Uninstall VirtualBox.
553 """
554 reporter.testStart('Uninstalling VirtualBox');
555
556 sHost = utils.getHostOs()
557 if sHost == 'darwin': fRc = self._uninstallVBoxOnDarwin();
558 elif sHost == 'linux': fRc = self._uninstallVBoxOnLinux();
559 elif sHost == 'solaris': fRc = self._uninstallVBoxOnSolaris(True);
560 elif sHost == 'win': fRc = self._uninstallVBoxOnWindows('uninstall');
561 else:
562 reporter.error('Unsupported host "%s".' % (sHost,));
563 if fRc is False and not fIgnoreError:
564 reporter.testFailure('Uninstallation failed.');
565
566 fRc2 = self._uninstallAllExtPacks();
567 if not fRc2 and fRc:
568 fRc = fRc2;
569
570 reporter.testDone(fSkipped = (fRc is None));
571 return fRc;
572
573 def _findFile(self, sRegExp, fMandatory = False):
574 """
575 Returns the first build file that matches the given regular expression
576 (basename only).
577
578 Returns None if no match was found, logging it as an error if
579 fMandatory is set.
580 """
581 oRegExp = re.compile(sRegExp);
582
583 reporter.log('_findFile: %s' % (sRegExp,));
584 for sFile in self._asBuildFiles:
585 if oRegExp.match(os.path.basename(sFile)) and os.path.exists(sFile):
586 return sFile;
587
588 # If we didn't unpack the build files, search all the files in the scratch area:
589 if not self._fUnpackedBuildFiles:
590 for sDir, _, asFiles in os.walk(self.sScratchPath):
591 for sFile in asFiles:
592 #reporter.log('_findFile: considering %s' % (sFile,));
593 if oRegExp.match(sFile):
594 return os.path.join(sDir, sFile);
595
596 if fMandatory:
597 reporter.error('Failed to find a file matching "%s" in %s.' % (sRegExp, self._asBuildFiles,));
598 return None;
599
600 def _waitForTestManagerConnectivity(self, cSecTimeout):
601 """
602 Check and wait for network connectivity to the test manager.
603
604 This is used with the windows installation and uninstallation since
605 these usually disrupts network connectivity when installing the filter
606 driver. If we proceed to quickly, we might finish the test at a time
607 when we cannot report to the test manager and thus end up with an
608 abandonded test error.
609 """
610 cSecElapsed = 0;
611 secStart = utils.timestampSecond();
612 while reporter.checkTestManagerConnection() is False:
613 cSecElapsed = utils.timestampSecond() - secStart;
614 if cSecElapsed >= cSecTimeout:
615 reporter.log('_waitForTestManagerConnectivity: Giving up after %u secs.' % (cSecTimeout,));
616 return False;
617 time.sleep(2);
618
619 if cSecElapsed > 0:
620 reporter.log('_waitForTestManagerConnectivity: Waited %s secs.' % (cSecTimeout,));
621 return True;
622
623
624 #
625 # Darwin (Mac OS X).
626 #
627
628 def _darwinDmgPath(self):
629 """ Returns the path to the DMG mount."""
630 return os.path.join(self.sScratchPath, 'DmgMountPoint');
631
632 def _darwinUnmountDmg(self, fIgnoreError):
633 """
634 Umount any DMG on at the default mount point.
635 """
636 sMountPath = self._darwinDmgPath();
637 if not os.path.exists(sMountPath):
638 return True;
639
640 # Unmount.
641 fRc = self._executeSync(['hdiutil', 'detach', sMountPath ]);
642 if not fRc and not fIgnoreError:
643 # In case it's busy for some reason or another, just retry after a little delay.
644 for iTry in range(6):
645 time.sleep(5);
646 reporter.error('Retry #%s unmount DMT at %s' % (iTry + 1, sMountPath,));
647 fRc = self._executeSync(['hdiutil', 'detach', sMountPath ]);
648 if fRc:
649 break;
650 if not fRc:
651 reporter.error('Failed to unmount DMG at %s' % (sMountPath,));
652
653 # Remove dir.
654 try:
655 os.rmdir(sMountPath);
656 except:
657 if not fIgnoreError:
658 reporter.errorXcpt('Failed to remove directory %s' % (sMountPath,));
659 return fRc;
660
661 def _darwinMountDmg(self, sDmg):
662 """
663 Mount the DMG at the default mount point.
664 """
665 self._darwinUnmountDmg(fIgnoreError = True)
666
667 sMountPath = self._darwinDmgPath();
668 if not os.path.exists(sMountPath):
669 try:
670 os.mkdir(sMountPath, 0o755);
671 except:
672 reporter.logXcpt();
673 return False;
674
675 return self._executeSync(['hdiutil', 'attach', '-readonly', '-mount', 'required', '-mountpoint', sMountPath, sDmg, ]);
676
677 def _generateWithoutKextsChoicesXmlOnDarwin(self):
678 """
679 Generates the choices XML when kernel drivers are disabled.
680 None is returned on failure.
681 """
682 sPath = os.path.join(self.sScratchPath, 'DarwinChoices.xml');
683 oFile = utils.openNoInherit(sPath, 'wt');
684 oFile.write('<?xml version="1.0" encoding="UTF-8"?>\n'
685 '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n'
686 '<plist version="1.0">\n'
687 '<array>\n'
688 ' <dict>\n'
689 ' <key>attributeSetting</key>\n'
690 ' <integer>0</integer>\n'
691 ' <key>choiceAttribute</key>\n'
692 ' <string>selected</string>\n'
693 ' <key>choiceIdentifier</key>\n'
694 ' <string>choiceVBoxKEXTs</string>\n'
695 ' </dict>\n'
696 '</array>\n'
697 '</plist>\n');
698 oFile.close();
699 return sPath;
700
701 def _installVBoxOnDarwin(self):
702 """ Installs VBox on Mac OS X."""
703
704 # TEMPORARY HACK - START
705 # Don't install the kernel drivers on the testboxes with BigSur and later
706 # Needs a more generic approach but that one needs more effort.
707 sHostName = socket.getfqdn();
708 if sHostName.startswith('testboxmac10') \
709 or sHostName.startswith('testboxmac11'):
710 self._fKernelDrivers = False;
711 # TEMPORARY HACK - END
712
713 sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
714 if sDmg is None:
715 return False;
716
717 # Mount the DMG.
718 fRc = self._darwinMountDmg(sDmg);
719 if fRc is not True:
720 return False;
721
722 # Uninstall any previous vbox version first.
723 sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
724 fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
725 if fRc is True:
726
727 # Install the package.
728 sPkg = os.path.join(self._darwinDmgPath(), 'VirtualBox.pkg');
729 if self._fKernelDrivers:
730 fRc, _ = self._sudoExecuteSync(['installer', '-verbose', '-dumplog', '-pkg', sPkg, '-target', '/']);
731 else:
732 sChoicesXml = self._generateWithoutKextsChoicesXmlOnDarwin();
733 if sChoicesXml is not None:
734 fRc, _ = self._sudoExecuteSync(['installer', '-verbose', '-dumplog', '-pkg', sPkg, \
735 '-applyChoiceChangesXML', sChoicesXml, '-target', '/']);
736 else:
737 fRc = False;
738
739 # Unmount the DMG and we're done.
740 if not self._darwinUnmountDmg(fIgnoreError = False):
741 fRc = False;
742 return fRc;
743
744 def _uninstallVBoxOnDarwin(self):
745 """ Uninstalls VBox on Mac OS X."""
746
747 # Is VirtualBox installed? If not, don't try uninstall it.
748 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
749 if sVBox is None:
750 return True;
751
752 # Find the dmg.
753 sDmg = self._findFile('^VirtualBox-.*\\.dmg$');
754 if sDmg is None:
755 return False;
756 if not os.path.exists(sDmg):
757 return True;
758
759 # Mount the DMG.
760 fRc = self._darwinMountDmg(sDmg);
761 if fRc is not True:
762 return False;
763
764 # Execute the uninstaller.
765 sUninstaller = os.path.join(self._darwinDmgPath(), 'VirtualBox_Uninstall.tool');
766 fRc, _ = self._sudoExecuteSync([sUninstaller, '--unattended',]);
767
768 # Unmount the DMG and we're done.
769 if not self._darwinUnmountDmg(fIgnoreError = False):
770 fRc = False;
771 return fRc;
772
773 #
774 # GNU/Linux
775 #
776
777 def _installVBoxOnLinux(self):
778 """ Installs VBox on Linux."""
779 sRun = self._findFile('^VirtualBox-.*\\.run$');
780 if sRun is None:
781 return False;
782 utils.chmodPlusX(sRun);
783
784 # Install the new one.
785 fRc, _ = self._sudoExecuteSync([sRun,]);
786 return fRc;
787
788 def _uninstallVBoxOnLinux(self):
789 """ Uninstalls VBox on Linux."""
790
791 # Is VirtualBox installed? If not, don't try uninstall it.
792 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
793 if sVBox is None:
794 return True;
795
796 # Find the .run file and use it.
797 sRun = self._findFile('^VirtualBox-.*\\.run$', fMandatory = False);
798 if sRun is not None:
799 utils.chmodPlusX(sRun);
800 fRc, _ = self._sudoExecuteSync([sRun, 'uninstall']);
801 return fRc;
802
803 # Try the installed uninstaller.
804 for sUninstaller in [os.path.join(sVBox, 'uninstall.sh'), '/opt/VirtualBox/uninstall.sh', ]:
805 if os.path.isfile(sUninstaller):
806 reporter.log('Invoking "%s"...' % (sUninstaller,));
807 fRc, _ = self._sudoExecuteSync([sUninstaller, 'uninstall']);
808 return fRc;
809
810 reporter.log('Did not find any VirtualBox install to uninstall.');
811 return True;
812
813
814 #
815 # Solaris
816 #
817
818 def _generateAutoResponseOnSolaris(self):
819 """
820 Generates an autoresponse file on solaris, returning the name.
821 None is return on failure.
822 """
823 sPath = os.path.join(self.sScratchPath, 'SolarisAutoResponse');
824 oFile = utils.openNoInherit(sPath, 'wt');
825 oFile.write('basedir=default\n'
826 'runlevel=nocheck\n'
827 'conflict=quit\n'
828 'setuid=nocheck\n'
829 'action=nocheck\n'
830 'partial=quit\n'
831 'instance=unique\n'
832 'idepend=quit\n'
833 'rdepend=quit\n'
834 'space=quit\n'
835 'mail=\n');
836 oFile.close();
837 return sPath;
838
839 def _installVBoxOnSolaris(self):
840 """ Installs VBox on Solaris."""
841 sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = False);
842 if sPkg is None:
843 sTar = self._findFile('^VirtualBox-.*-SunOS-.*\\.tar.gz$', fMandatory = False);
844 if sTar is not None:
845 if self._maybeUnpackArchive(sTar) is not True:
846 return False;
847 sPkg = self._findFile('^VirtualBox-.*\\.pkg$', fMandatory = True);
848 sRsp = self._findFile('^autoresponse$', fMandatory = True);
849 if sPkg is None or sRsp is None:
850 return False;
851
852 # Uninstall first (ignore result).
853 self._uninstallVBoxOnSolaris(False);
854
855 # Install the new one.
856 fRc, _ = self._sudoExecuteSync(['pkgadd', '-d', sPkg, '-n', '-a', sRsp, 'SUNWvbox']);
857 return fRc;
858
859 def _uninstallVBoxOnSolaris(self, fRestartSvcConfigD):
860 """ Uninstalls VBox on Solaris."""
861 reporter.flushall();
862 if utils.processCall(['pkginfo', '-q', 'SUNWvbox']) != 0:
863 return True;
864 sRsp = self._generateAutoResponseOnSolaris();
865 fRc, _ = self._sudoExecuteSync(['pkgrm', '-n', '-a', sRsp, 'SUNWvbox']);
866
867 #
868 # Restart the svc.configd as it has a tendency to clog up with time and
869 # become unresponsive. It will handle SIGHUP by exiting the sigwait()
870 # look in the main function and shut down the service nicely (backend_fini).
871 # The restarter will then start a new instance of it.
872 #
873 if fRestartSvcConfigD:
874 time.sleep(1); # Give it a chance to flush pkgrm stuff.
875 self._sudoExecuteSync(['pkill', '-HUP', 'svc.configd']);
876 time.sleep(5); # Spare a few cpu cycles it to shutdown and restart.
877
878 return fRc;
879
880 #
881 # Windows
882 #
883
884 ## VBox windows services we can query the status of.
885 kasWindowsServices = [ 'vboxsup', 'vboxusbmon', 'vboxnetadp', 'vboxnetflt', 'vboxnetlwf' ];
886
887 def _installVBoxOnWindows(self):
888 """ Installs VBox on Windows."""
889 sExe = self._findFile('^VirtualBox-.*-(MultiArch|Win).exe$');
890 if sExe is None:
891 return False;
892
893 # TEMPORARY HACK - START
894 # It seems that running the NDIS cleanup script upon uninstallation is not
895 # a good idea, so let's run it before installing VirtualBox.
896 #sHostName = socket.getfqdn();
897 #if not sHostName.startswith('testboxwin3') \
898 # and not sHostName.startswith('testboxharp2') \
899 # and not sHostName.startswith('wei01-b6ka-3') \
900 # and utils.getHostOsVersion() in ['8', '8.1', '9', '2008Server', '2008ServerR2', '2012Server']:
901 # reporter.log('Peforming extra NDIS cleanup...');
902 # sMagicScript = os.path.abspath(os.path.join(g_ksValidationKitDir, 'testdriver', 'win-vbox-net-uninstall.ps1'));
903 # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-Command', 'set-executionpolicy unrestricted']);
904 # if not fRc2:
905 # reporter.log('set-executionpolicy failed.');
906 # self._sudoExecuteSync(['powershell.exe', '-Command', 'get-executionpolicy']);
907 # fRc2, _ = self._sudoExecuteSync(['powershell.exe', '-File', sMagicScript]);
908 # if not fRc2:
909 # reporter.log('NDIS cleanup failed.');
910 # TEMPORARY HACK - END
911
912 # Uninstall any previous vbox version first.
913 fRc = self._uninstallVBoxOnWindows('install');
914 if fRc is not True:
915 return None; # There shouldn't be anything to uninstall, and if there is, it's not our fault.
916
917 # We need the help text to detect supported options below.
918 reporter.log('Executing: %s' % ([sExe, '--silent', '--help'], ));
919 reporter.flushall();
920 (iExitCode, sHelp, _) = utils.processOutputUnchecked([sExe, '--silent', '--help'], fIgnoreEncoding = True);
921 reporter.log('Exit code: %d, %u chars of help text' % (iExitCode, len(sHelp),));
922
923 # Gather installer arguments.
924 asArgs = [sExe, '-vvvv', '--silent', '--logging'];
925 asArgs.extend(['--msiparams', 'REBOOT=ReallySuppress']);
926 sVBoxInstallPath = os.environ.get('VBOX_INSTALL_PATH', None);
927 if sVBoxInstallPath is not None:
928 asArgs.extend(['INSTALLDIR="%s"' % (sVBoxInstallPath,)]);
929
930 if sHelp.find("--msi-log-file") >= 0:
931 sLogFile = os.path.join(self.sScratchPath, 'VBoxInstallLog.txt'); # Specify location to prevent a random one.
932 asArgs.extend(['--msi-log-file', sLogFile]);
933 else:
934 sLogFile = os.path.join(tempfile.gettempdir(), 'VirtualBox', 'VBoxInstallLog.txt'); # Hardcoded TMP location.
935
936 if self._fWinForcedInstallTimestampCA and sHelp.find("--force-install-timestamp-ca") >= 0:
937 asArgs.extend(['--force-install-timestamp-ca']);
938
939 # Install it.
940 fRc2, iRc = self._sudoExecuteSync(asArgs);
941 if fRc2 is False:
942 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
943 reporter.error('Installer required a reboot to complete installation (ERROR_SUCCESS_REBOOT_REQUIRED)');
944 else:
945 reporter.error('Installer failed, exit code: %s' % (iRc,));
946 fRc = False;
947
948 # Add the installer log if present and wait for the network connection to be restore after the filter driver upset.
949 if os.path.isfile(sLogFile):
950 reporter.addLogFile(sLogFile, 'log/installer', "Verbose MSI installation log file");
951 self._waitForTestManagerConnectivity(30);
952
953 return fRc;
954
955 def _isProcessPresent(self, sName):
956 """ Checks whether the named process is present or not. """
957 for oProcess in utils.processListAll():
958 sBase = oProcess.getBaseImageNameNoExeSuff();
959 if sBase is not None and sBase.lower() == sName:
960 return True;
961 return False;
962
963 def _killProcessesByName(self, sName, sDesc, fChildren = False):
964 """ Kills the named process, optionally including children. """
965 cKilled = 0;
966 aoProcesses = utils.processListAll();
967 for oProcess in aoProcesses:
968 sBase = oProcess.getBaseImageNameNoExeSuff();
969 if sBase is not None and sBase.lower() == sName:
970 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
971 utils.processKill(oProcess.iPid);
972 cKilled += 1;
973
974 if fChildren:
975 for oChild in aoProcesses:
976 if oChild.iParentPid == oProcess.iPid and oChild.iParentPid is not None:
977 reporter.log('Killing %s child process: %s (%s)' % (sDesc, oChild.iPid, sBase));
978 utils.processKill(oChild.iPid);
979 cKilled += 1;
980 return cKilled;
981
982 def _terminateProcessesByNameAndArgSubstr(self, sName, sArg, sDesc):
983 """
984 Terminates the named process using taskkill.exe, if any of its args
985 contains the passed string.
986 """
987 cKilled = 0;
988 aoProcesses = utils.processListAll();
989 for oProcess in aoProcesses:
990 sBase = oProcess.getBaseImageNameNoExeSuff();
991 if sBase is not None and sBase.lower() == sName and any(sArg in s for s in oProcess.asArgs):
992
993 reporter.log('Killing %s process: %s (%s)' % (sDesc, oProcess.iPid, sBase));
994 self._executeSync(['taskkill.exe', '/pid', '%u' % (oProcess.iPid,)]);
995 cKilled += 1;
996 return cKilled;
997
998 def _uninstallVBoxOnWindows(self, sMode):
999 """
1000 Uninstalls VBox on Windows, all installations we find to be on the safe side...
1001 """
1002 assert sMode in ['install', 'uninstall',];
1003
1004 import win32com.client; # pylint: disable=import-error
1005 win32com.client.gencache.EnsureModule('{000C1092-0000-0000-C000-000000000046}', 1033, 1, 0);
1006 oInstaller = win32com.client.Dispatch('WindowsInstaller.Installer',
1007 resultCLSID = '{000C1090-0000-0000-C000-000000000046}')
1008
1009 # Search installed products for VirtualBox.
1010 asProdCodes = [];
1011 for sProdCode in oInstaller.Products:
1012 try:
1013 sProdName = oInstaller.ProductInfo(sProdCode, "ProductName");
1014 except:
1015 reporter.logXcpt();
1016 continue;
1017 #reporter.log('Info: %s=%s' % (sProdCode, sProdName));
1018 if sProdName.startswith('Oracle VM VirtualBox') \
1019 or sProdName.startswith('Sun VirtualBox'):
1020 asProdCodes.append([sProdCode, sProdName]);
1021
1022 # Before we start uninstalling anything, just ruthlessly kill any cdb,
1023 # msiexec, drvinst and some rundll process we might find hanging around.
1024 if self._isProcessPresent('rundll32'):
1025 cTimes = 0;
1026 while cTimes < 3:
1027 cTimes += 1;
1028 cKilled = self._terminateProcessesByNameAndArgSubstr('rundll32', 'InstallSecurityPromptRunDllW',
1029 'MSI driver installation');
1030 if cKilled <= 0:
1031 break;
1032 time.sleep(10); # Give related drvinst process a chance to clean up after we killed the verification dialog.
1033
1034 if self._isProcessPresent('drvinst'):
1035 time.sleep(15); # In the hope that it goes away.
1036 cTimes = 0;
1037 while cTimes < 4:
1038 cTimes += 1;
1039 cKilled = self._killProcessesByName('drvinst', 'MSI driver installation', True);
1040 if cKilled <= 0:
1041 break;
1042 time.sleep(10); # Give related MSI process a chance to clean up after we killed the driver installer.
1043
1044 if self._isProcessPresent('msiexec'):
1045 cTimes = 0;
1046 while cTimes < 3:
1047 reporter.log('found running msiexec process, waiting a bit...');
1048 time.sleep(20) # In the hope that it goes away.
1049 if not self._isProcessPresent('msiexec'):
1050 break;
1051 cTimes += 1;
1052 ## @todo this could also be the msiexec system service, try to detect this case!
1053 if cTimes >= 6:
1054 cKilled = self._killProcessesByName('msiexec', 'MSI driver installation');
1055 if cKilled > 0:
1056 time.sleep(16); # fudge.
1057
1058 # cdb.exe sometimes stays running (from utils.getProcessInfo), blocking
1059 # the scratch directory. No idea why.
1060 if self._isProcessPresent('cdb'):
1061 cTimes = 0;
1062 while cTimes < 3:
1063 cKilled = self._killProcessesByName('cdb', 'cdb.exe from getProcessInfo');
1064 if cKilled <= 0:
1065 break;
1066 time.sleep(2); # fudge.
1067
1068 # Do the uninstalling.
1069 fRc = True;
1070 sLogFile = os.path.join(self.sScratchPath, 'VBoxUninstallLog.txt');
1071 for sProdCode, sProdName in asProdCodes:
1072 reporter.log('Uninstalling %s (%s)...' % (sProdName, sProdCode));
1073 fRc2, iRc = self._sudoExecuteSync(['msiexec', '/uninstall', sProdCode, '/quiet', '/passive', '/norestart',
1074 '/L*v', '%s' % (sLogFile), ]);
1075 if fRc2 is False:
1076 if iRc == 3010: # ERROR_SUCCESS_REBOOT_REQUIRED
1077 reporter.error('Uninstaller required a reboot to complete uninstallation');
1078 else:
1079 reporter.error('Uninstaller failed, exit code: %s' % (iRc,));
1080 fRc = False;
1081
1082 self._waitForTestManagerConnectivity(30);
1083
1084 # Upload the log on failure. Do it early if the extra cleanups below causes trouble.
1085 if fRc is False and os.path.isfile(sLogFile):
1086 reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
1087 sLogFile = None;
1088
1089 # Log driver service states (should ls \Driver\VBox* and \Device\VBox*).
1090 fHadLeftovers = False;
1091 asLeftovers = [];
1092 for sService in reversed(self.kasWindowsServices):
1093 cTries = 0;
1094 while True:
1095 fRc2, _ = self._sudoExecuteSync(['sc.exe', 'query', sService]);
1096 if not fRc2:
1097 break;
1098 fHadLeftovers = True;
1099
1100 cTries += 1;
1101 if cTries > 3:
1102 asLeftovers.append(sService,);
1103 break;
1104
1105 # Get the status output.
1106 try:
1107 sOutput = utils.sudoProcessOutputChecked(['sc.exe', 'query', sService]);
1108 except:
1109 reporter.logXcpt();
1110 else:
1111 if re.search(r'STATE\s+:\s*1\s*STOPPED', sOutput) is None:
1112 reporter.log('Trying to stop %s...' % (sService,));
1113 fRc2, _ = self._sudoExecuteSync(['sc.exe', 'stop', sService]);
1114 time.sleep(1); # fudge
1115
1116 reporter.log('Trying to delete %s...' % (sService,));
1117 self._sudoExecuteSync(['sc.exe', 'delete', sService]);
1118
1119 time.sleep(1); # fudge
1120
1121 if asLeftovers:
1122 reporter.log('Warning! Leftover VBox drivers: %s' % (', '.join(asLeftovers),));
1123 fRc = False;
1124
1125 if fHadLeftovers:
1126 self._waitForTestManagerConnectivity(30);
1127
1128 # Upload the log if we have any leftovers and didn't upload it already.
1129 if sLogFile is not None and (fRc is False or fHadLeftovers) and os.path.isfile(sLogFile):
1130 reporter.addLogFile(sLogFile, 'log/uninstaller', "Verbose MSI uninstallation log file");
1131
1132 return fRc;
1133
1134
1135 #
1136 # Extension pack.
1137 #
1138
1139 def _getVBoxInstallPath(self, fFailIfNotFound):
1140 """ Returns the default VBox installation path. """
1141 sHost = utils.getHostOs();
1142 if sHost == 'win':
1143 sProgFiles = os.environ.get('ProgramFiles', 'C:\\Program Files');
1144 asLocs = [
1145 os.path.join(sProgFiles, 'Oracle', 'VirtualBox'),
1146 os.path.join(sProgFiles, 'OracleVM', 'VirtualBox'),
1147 os.path.join(sProgFiles, 'Sun', 'VirtualBox'),
1148 ];
1149 elif sHost in ('linux', 'solaris',):
1150 asLocs = [ '/opt/VirtualBox', '/opt/VirtualBox-3.2', '/opt/VirtualBox-3.1', '/opt/VirtualBox-3.0'];
1151 elif sHost == 'darwin':
1152 asLocs = [ '/Applications/VirtualBox.app/Contents/MacOS' ];
1153 else:
1154 asLocs = [ '/opt/VirtualBox' ];
1155 if 'VBOX_INSTALL_PATH' in os.environ:
1156 asLocs.insert(0, os.environ.get('VBOX_INSTALL_PATH', None));
1157
1158 for sLoc in asLocs:
1159 if os.path.isdir(sLoc):
1160 return sLoc;
1161 if fFailIfNotFound:
1162 reporter.error('Failed to locate VirtualBox installation: %s' % (asLocs,));
1163 else:
1164 reporter.log2('Failed to locate VirtualBox installation: %s' % (asLocs,));
1165 return None;
1166
1167 def _installExtPack(self):
1168 """ Installs the extension pack. """
1169 sVBox = self._getVBoxInstallPath(fFailIfNotFound = True);
1170 if sVBox is None:
1171 return False;
1172 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
1173
1174 if self._uninstallAllExtPacks() is not True:
1175 return False;
1176
1177 sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.vbox-extpack');
1178 if sExtPack is None:
1179 sExtPack = self._findFile('Oracle_VM_VirtualBox_Extension_Pack.*.vbox-extpack');
1180 if sExtPack is None:
1181 return True;
1182
1183 sDstDir = os.path.join(sExtPackDir, 'Oracle_VM_VirtualBox_Extension_Pack');
1184 reporter.log('Installing extension pack "%s" to "%s"...' % (sExtPack, sExtPackDir));
1185 fRc, _ = self._sudoExecuteSync([ self.getBinTool('vts_tar'),
1186 '--extract',
1187 '--verbose',
1188 '--gzip',
1189 '--file', sExtPack,
1190 '--directory', sDstDir,
1191 '--file-mode-and-mask', '0644',
1192 '--file-mode-or-mask', '0644',
1193 '--dir-mode-and-mask', '0755',
1194 '--dir-mode-or-mask', '0755',
1195 '--owner', '0',
1196 '--group', '0',
1197 ]);
1198 return fRc;
1199
1200 def _uninstallAllExtPacks(self):
1201 """ Uninstalls all extension packs. """
1202 sVBox = self._getVBoxInstallPath(fFailIfNotFound = False);
1203 if sVBox is None:
1204 return True;
1205
1206 sExtPackDir = os.path.join(sVBox, 'ExtensionPacks');
1207 if not os.path.exists(sExtPackDir):
1208 return True;
1209
1210 fRc, _ = self._sudoExecuteSync([self.getBinTool('vts_rm'), '-Rfv', '--', sExtPackDir]);
1211 return fRc;
1212
1213
1214
1215if __name__ == '__main__':
1216 sys.exit(VBoxInstallerTestDriver().main(sys.argv));
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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