VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/common/utils.py@ 71157

Last change on this file since 71157 was 71157, checked in by vboxsync, 7 years ago

utils.py,vcs_import.py: Added printOut and printErr to utils for dealing console encoding issues in python2; use it in vcs_import.py.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 68.6 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: utils.py 71157 2018-02-28 15:38:15Z vboxsync $
3# pylint: disable=C0302
4
5"""
6Common Utility Functions.
7"""
8
9from __future__ import print_function;
10
11__copyright__ = \
12"""
13Copyright (C) 2012-2017 Oracle Corporation
14
15This file is part of VirtualBox Open Source Edition (OSE), as
16available from http://www.virtualbox.org. This file is free software;
17you can redistribute it and/or modify it under the terms of the GNU
18General Public License (GPL) as published by the Free Software
19Foundation, in version 2 as it comes in the "COPYING" file of the
20VirtualBox OSE distribution. VirtualBox OSE is distributed in the
21hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
22
23The contents of this file may alternatively be used under the terms
24of the Common Development and Distribution License Version 1.0
25(CDDL) only, as it comes in the "COPYING.CDDL" file of the
26VirtualBox OSE distribution, in which case the provisions of the
27CDDL are applicable instead of those of the GPL.
28
29You may elect to license modified versions of this file under the
30terms and conditions of either the GPL or the CDDL or both.
31"""
32__version__ = "$Revision: 71157 $"
33
34
35# Standard Python imports.
36import datetime;
37import os;
38import platform;
39import re;
40import stat;
41import subprocess;
42import sys;
43import time;
44import traceback;
45import unittest;
46
47if sys.platform == 'win32':
48 import ctypes;
49 import msvcrt; # pylint: disable=import-error
50 import win32api; # pylint: disable=import-error
51 import win32con; # pylint: disable=import-error
52 import win32console; # pylint: disable=import-error
53 import win32file; # pylint: disable=import-error
54 import win32process; # pylint: disable=import-error
55 import winerror; # pylint: disable=import-error
56 import pywintypes; # pylint: disable=import-error
57else:
58 import signal;
59
60# Python 3 hacks:
61if sys.version_info[0] >= 3:
62 unicode = str; # pylint: disable=redefined-builtin,invalid-name
63 xrange = range; # pylint: disable=redefined-builtin,invalid-name
64 long = int; # pylint: disable=redefined-builtin,invalid-name
65
66
67#
68# Output.
69#
70
71def printOut(sString):
72 """
73 Outputs a string to standard output, dealing with python 2.x encoding stupidity.
74 """
75 sStreamEncoding = sys.stdout.encoding;
76 if sStreamEncoding == 'UTF-8' or not isinstance(sString, unicode):
77 print(sString);
78 else:
79 print(sString.encode(sStreamEncoding, 'backslashreplace').decode(sStreamEncoding));
80
81def printErr(sString):
82 """
83 Outputs a string to standard error, dealing with python 2.x encoding stupidity.
84 """
85 sStreamEncoding = sys.stderr.encoding;
86 if sStreamEncoding == 'UTF-8' or not isinstance(sString, unicode):
87 print(sString, file = sys.stderr);
88 else:
89 print(sString.encode(sStreamEncoding, 'backslashreplace').decode(sStreamEncoding), file = sys.stderr);
90
91
92#
93# Host OS and CPU.
94#
95
96def getHostOs():
97 """
98 Gets the host OS name (short).
99
100 See the KBUILD_OSES variable in kBuild/header.kmk for possible return values.
101 """
102 sPlatform = platform.system();
103 if sPlatform in ('Linux', 'Darwin', 'Solaris', 'FreeBSD', 'NetBSD', 'OpenBSD'):
104 sPlatform = sPlatform.lower();
105 elif sPlatform == 'Windows':
106 sPlatform = 'win';
107 elif sPlatform == 'SunOS':
108 sPlatform = 'solaris';
109 else:
110 raise Exception('Unsupported platform "%s"' % (sPlatform,));
111 return sPlatform;
112
113g_sHostArch = None;
114
115def getHostArch():
116 """
117 Gets the host CPU architecture.
118
119 See the KBUILD_ARCHES variable in kBuild/header.kmk for possible return values.
120 """
121 global g_sHostArch;
122 if g_sHostArch is None:
123 sArch = platform.machine();
124 if sArch in ('i386', 'i486', 'i586', 'i686', 'i786', 'i886', 'x86'):
125 sArch = 'x86';
126 elif sArch in ('AMD64', 'amd64', 'x86_64'):
127 sArch = 'amd64';
128 elif sArch == 'i86pc': # SunOS
129 if platform.architecture()[0] == '64bit':
130 sArch = 'amd64';
131 else:
132 try:
133 sArch = str(processOutputChecked(['/usr/bin/isainfo', '-n',]));
134 except:
135 pass;
136 sArch = sArch.strip();
137 if sArch != 'amd64':
138 sArch = 'x86';
139 else:
140 raise Exception('Unsupported architecture/machine "%s"' % (sArch,));
141 g_sHostArch = sArch;
142 return g_sHostArch;
143
144
145def getHostOsDotArch():
146 """
147 Gets the 'os.arch' for the host.
148 """
149 return '%s.%s' % (getHostOs(), getHostArch());
150
151
152def isValidOs(sOs):
153 """
154 Validates the OS name.
155 """
156 if sOs in ('darwin', 'dos', 'dragonfly', 'freebsd', 'haiku', 'l4', 'linux', 'netbsd', 'nt', 'openbsd', \
157 'os2', 'solaris', 'win', 'os-agnostic'):
158 return True;
159 return False;
160
161
162def isValidArch(sArch):
163 """
164 Validates the CPU architecture name.
165 """
166 if sArch in ('x86', 'amd64', 'sparc32', 'sparc64', 's390', 's390x', 'ppc32', 'ppc64', \
167 'mips32', 'mips64', 'ia64', 'hppa32', 'hppa64', 'arm', 'alpha'):
168 return True;
169 return False;
170
171def isValidOsDotArch(sOsDotArch):
172 """
173 Validates the 'os.arch' string.
174 """
175
176 asParts = sOsDotArch.split('.');
177 if asParts.length() != 2:
178 return False;
179 return isValidOs(asParts[0]) \
180 and isValidArch(asParts[1]);
181
182def getHostOsVersion():
183 """
184 Returns the host OS version. This is platform.release with additional
185 distro indicator on linux.
186 """
187 sVersion = platform.release();
188 sOs = getHostOs();
189 if sOs == 'linux':
190 sDist = '';
191 try:
192 # try /etc/lsb-release first to distinguish between Debian and Ubuntu
193 oFile = open('/etc/lsb-release');
194 for sLine in oFile:
195 oMatch = re.search(r'(?:DISTRIB_DESCRIPTION\s*=)\s*"*(.*)"', sLine);
196 if oMatch is not None:
197 sDist = oMatch.group(1).strip();
198 except:
199 pass;
200 if sDist:
201 sVersion += ' / ' + sDist;
202 else:
203 asFiles = \
204 [
205 [ '/etc/debian_version', 'Debian v'],
206 [ '/etc/gentoo-release', '' ],
207 [ '/etc/oracle-release', '' ],
208 [ '/etc/redhat-release', '' ],
209 [ '/etc/SuSE-release', '' ],
210 ];
211 for sFile, sPrefix in asFiles:
212 if os.path.isfile(sFile):
213 try:
214 oFile = open(sFile);
215 sLine = oFile.readline();
216 oFile.close();
217 except:
218 continue;
219 sLine = sLine.strip()
220 if sLine:
221 sVersion += ' / ' + sPrefix + sLine;
222 break;
223
224 elif sOs == 'solaris':
225 sVersion = platform.version();
226 if os.path.isfile('/etc/release'):
227 try:
228 oFile = open('/etc/release');
229 sLast = oFile.readlines()[-1];
230 oFile.close();
231 sLast = sLast.strip();
232 if sLast:
233 sVersion += ' (' + sLast + ')';
234 except:
235 pass;
236
237 elif sOs == 'darwin':
238 sOsxVersion = platform.mac_ver()[0];
239 codenames = {"4": "Tiger",
240 "5": "Leopard",
241 "6": "Snow Leopard",
242 "7": "Lion",
243 "8": "Mountain Lion",
244 "9": "Mavericks",
245 "10": "Yosemite",
246 "11": "El Capitan",
247 "12": "Sierra",
248 "13": "High Sierra",
249 "14": "Unknown 14", }
250 sVersion += ' / OS X ' + sOsxVersion + ' (' + codenames[sOsxVersion.split('.')[1]] + ')'
251
252 elif sOs == 'win':
253 class OSVersionInfoEx(ctypes.Structure):
254 """ OSVERSIONEX """
255 kaFields = [
256 ('dwOSVersionInfoSize', ctypes.c_ulong),
257 ('dwMajorVersion', ctypes.c_ulong),
258 ('dwMinorVersion', ctypes.c_ulong),
259 ('dwBuildNumber', ctypes.c_ulong),
260 ('dwPlatformId', ctypes.c_ulong),
261 ('szCSDVersion', ctypes.c_wchar*128),
262 ('wServicePackMajor', ctypes.c_ushort),
263 ('wServicePackMinor', ctypes.c_ushort),
264 ('wSuiteMask', ctypes.c_ushort),
265 ('wProductType', ctypes.c_byte),
266 ('wReserved', ctypes.c_byte)]
267 _fields_ = kaFields # pylint: disable=invalid-name
268
269 def __init__(self):
270 super(OSVersionInfoEx, self).__init__()
271 self.dwOSVersionInfoSize = ctypes.sizeof(self)
272
273 oOsVersion = OSVersionInfoEx()
274 rc = ctypes.windll.Ntdll.RtlGetVersion(ctypes.byref(oOsVersion))
275 if rc == 0:
276 # Python platform.release() is not reliable for newer server releases
277 if oOsVersion.wProductType != 1:
278 if oOsVersion.dwMajorVersion == 10 and oOsVersion.dwMinorVersion == 0:
279 sVersion = '2016Server';
280 elif oOsVersion.dwMajorVersion == 6 and oOsVersion.dwMinorVersion == 3:
281 sVersion = '2012ServerR2';
282 elif oOsVersion.dwMajorVersion == 6 and oOsVersion.dwMinorVersion == 2:
283 sVersion = '2012Server';
284 elif oOsVersion.dwMajorVersion == 6 and oOsVersion.dwMinorVersion == 1:
285 sVersion = '2008ServerR2';
286 elif oOsVersion.dwMajorVersion == 6 and oOsVersion.dwMinorVersion == 0:
287 sVersion = '2008Server';
288 elif oOsVersion.dwMajorVersion == 5 and oOsVersion.dwMinorVersion == 2:
289 sVersion = '2003Server';
290 sVersion += ' build ' + str(oOsVersion.dwBuildNumber)
291 if oOsVersion.wServicePackMajor:
292 sVersion += ' SP' + str(oOsVersion.wServicePackMajor)
293 if oOsVersion.wServicePackMinor:
294 sVersion += '.' + str(oOsVersion.wServicePackMinor)
295
296 return sVersion;
297
298#
299# File system.
300#
301
302def openNoInherit(sFile, sMode = 'r'):
303 """
304 Wrapper around open() that tries it's best to make sure the file isn't
305 inherited by child processes.
306
307 This is a best effort thing at the moment as it doesn't synchronizes with
308 child process spawning in any way. Thus it can be subject to races in
309 multithreaded programs.
310 """
311
312 # Python 3.4 and later automatically creates non-inherit handles. See PEP-0446.
313 uPythonVer = (sys.version_info[0] << 16) | (sys.version_info[1] & 0xffff);
314 if uPythonVer >= ((3 << 16) | 4):
315 oFile = open(sFile, sMode);
316 else:
317 try:
318 from fcntl import FD_CLOEXEC, F_GETFD, F_SETFD, fcntl; # pylint: disable=F0401
319 except:
320 # On windows, we can use the 'N' flag introduced in Visual C++ 7.0 or 7.1 with python 2.x.
321 if getHostOs() == 'win':
322 if uPythonVer < (3 << 16):
323 offComma = sMode.find(',');
324 if offComma < 0:
325 return open(sFile, sMode + 'N');
326 return open(sFile, sMode[:offComma] + 'N' + sMode[offComma:]);
327
328 # Just in case.
329 return open(sFile, sMode);
330
331 oFile = open(sFile, sMode);
332 #try:
333 fcntl(oFile, F_SETFD, fcntl(oFile, F_GETFD) | FD_CLOEXEC);
334 #except:
335 # pass;
336 return oFile;
337
338def openNoDenyDeleteNoInherit(sFile, sMode = 'r'):
339 """
340 Wrapper around open() that tries it's best to make sure the file isn't
341 inherited by child processes.
342
343 This is a best effort thing at the moment as it doesn't synchronizes with
344 child process spawning in any way. Thus it can be subject to races in
345 multithreaded programs.
346 """
347
348 if getHostOs() == 'win':
349 # Need to use CreateFile directly to open the file so we can feed it FILE_SHARE_DELETE.
350 # pylint: disable=no-member,c-extension-no-member
351 fAccess = 0;
352 fDisposition = win32file.OPEN_EXISTING;
353 if 'r' in sMode or '+' in sMode:
354 fAccess |= win32file.GENERIC_READ;
355 if 'a' in sMode:
356 fAccess |= win32file.GENERIC_WRITE;
357 fDisposition = win32file.OPEN_ALWAYS;
358 elif 'w' in sMode:
359 fAccess = win32file.GENERIC_WRITE;
360 if '+' in sMode:
361 fDisposition = win32file.OPEN_ALWAYS;
362 fAccess |= win32file.GENERIC_READ;
363 else:
364 fDisposition = win32file.CREATE_ALWAYS;
365 if not fAccess:
366 fAccess |= win32file.GENERIC_READ;
367 fSharing = (win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE
368 | win32file.FILE_SHARE_DELETE);
369 hFile = win32file.CreateFile(sFile, fAccess, fSharing, None, fDisposition, 0, None);
370 if 'a' in sMode:
371 win32file.SetFilePointer(hFile, 0, win32file.FILE_END);
372
373 # Turn the NT handle into a CRT file descriptor.
374 hDetachedFile = hFile.Detach();
375 if fAccess == win32file.GENERIC_READ:
376 fOpen = os.O_RDONLY;
377 elif fAccess == win32file.GENERIC_WRITE:
378 fOpen = os.O_WRONLY;
379 else:
380 fOpen = os.O_RDWR;
381 # pulint: enable=no-member,c-extension-no-member
382 if 'a' in sMode:
383 fOpen |= os.O_APPEND;
384 if 'b' in sMode or 't' in sMode:
385 fOpen |= os.O_TEXT; # pylint: disable=no-member
386 fdFile = msvcrt.open_osfhandle(hDetachedFile, fOpen);
387
388 # Tell python to use this handle.
389 oFile = os.fdopen(fdFile, sMode);
390 else:
391 oFile = open(sFile, sMode);
392
393 # Python 3.4 and later automatically creates non-inherit handles. See PEP-0446.
394 uPythonVer = (sys.version_info[0] << 16) | (sys.version_info[1] & 0xffff);
395 if uPythonVer < ((3 << 16) | 4):
396 try:
397 from fcntl import FD_CLOEXEC, F_GETFD, F_SETFD, fcntl; # pylint: disable=F0401
398 except:
399 pass;
400 else:
401 fcntl(oFile, F_SETFD, fcntl(oFile, F_GETFD) | FD_CLOEXEC);
402 return oFile;
403
404def noxcptReadLink(sPath, sXcptRet, sEncoding = 'utf-8'):
405 """
406 No exceptions os.readlink wrapper.
407 """
408 try:
409 sRet = os.readlink(sPath); # pylint: disable=E1101
410 except:
411 return sXcptRet;
412 if hasattr(sRet, 'decode'):
413 sRet = sRet.decode(sEncoding, 'ignore');
414 return sRet;
415
416def readFile(sFile, sMode = 'rb'):
417 """
418 Reads the entire file.
419 """
420 oFile = open(sFile, sMode);
421 sRet = oFile.read();
422 oFile.close();
423 return sRet;
424
425def noxcptReadFile(sFile, sXcptRet, sMode = 'rb', sEncoding = 'utf-8'):
426 """
427 No exceptions common.readFile wrapper.
428 """
429 try:
430 sRet = readFile(sFile, sMode);
431 except:
432 sRet = sXcptRet;
433 if sEncoding is not None and hasattr(sRet, 'decode'):
434 sRet = sRet.decode(sEncoding, 'ignore');
435 return sRet;
436
437def noxcptRmDir(sDir, oXcptRet = False):
438 """
439 No exceptions os.rmdir wrapper.
440 """
441 oRet = True;
442 try:
443 os.rmdir(sDir);
444 except:
445 oRet = oXcptRet;
446 return oRet;
447
448def noxcptDeleteFile(sFile, oXcptRet = False):
449 """
450 No exceptions os.remove wrapper.
451 """
452 oRet = True;
453 try:
454 os.remove(sFile);
455 except:
456 oRet = oXcptRet;
457 return oRet;
458
459
460def dirEnumerateTree(sDir, fnCallback, fIgnoreExceptions = True):
461 # type: (string, (string, stat) -> bool) -> bool
462 """
463 Recursively walks a directory tree, calling fnCallback for each.
464
465 fnCallback takes a full path and stat object (can be None). It
466 returns a boolean value, False stops walking and returns immediately.
467
468 Returns True or False depending on fnCallback.
469 Returns None fIgnoreExceptions is True and an exception was raised by listdir.
470 """
471 def __worker(sCurDir):
472 """ Worker for """
473 try:
474 asNames = os.listdir(sCurDir);
475 except:
476 if not fIgnoreExceptions:
477 raise;
478 return None;
479 rc = True;
480 for sName in asNames:
481 if sName not in [ '.', '..' ]:
482 sFullName = os.path.join(sCurDir, sName);
483 try: oStat = os.lstat(sFullName);
484 except: oStat = None;
485 if fnCallback(sFullName, oStat) is False:
486 return False;
487 if oStat is not None and stat.S_ISDIR(oStat.st_mode):
488 rc = __worker(sFullName);
489 if rc is False:
490 break;
491 return rc;
492
493 # Ensure unicode path here so listdir also returns unicode on windows.
494 ## @todo figure out unicode stuff on non-windows.
495 if sys.platform == 'win32':
496 sDir = unicode(sDir);
497 return __worker(sDir);
498
499
500
501def formatFileMode(uMode):
502 # type: (int) -> string
503 """
504 Format a st_mode value 'ls -la' fasion.
505 Returns string.
506 """
507 if stat.S_ISDIR(uMode): sMode = 'd';
508 elif stat.S_ISREG(uMode): sMode = '-';
509 elif stat.S_ISLNK(uMode): sMode = 'l';
510 elif stat.S_ISFIFO(uMode): sMode = 'p';
511 elif stat.S_ISCHR(uMode): sMode = 'c';
512 elif stat.S_ISBLK(uMode): sMode = 'b';
513 elif stat.S_ISSOCK(uMode): sMode = 's';
514 else: sMode = '?';
515 ## @todo sticky bits.
516 sMode += 'r' if uMode & stat.S_IRUSR else '-';
517 sMode += 'w' if uMode & stat.S_IWUSR else '-';
518 sMode += 'x' if uMode & stat.S_IXUSR else '-';
519 sMode += 'r' if uMode & stat.S_IRGRP else '-';
520 sMode += 'w' if uMode & stat.S_IWGRP else '-';
521 sMode += 'x' if uMode & stat.S_IXGRP else '-';
522 sMode += 'r' if uMode & stat.S_IROTH else '-';
523 sMode += 'w' if uMode & stat.S_IWOTH else '-';
524 sMode += 'x' if uMode & stat.S_IXOTH else '-';
525 sMode += ' ';
526 return sMode;
527
528
529def formatFileStat(oStat):
530 # type: (stat) -> string
531 """
532 Format a stat result 'ls -la' fasion (numeric IDs).
533 Returns string.
534 """
535 return '%s %3s %4s %4s %10s %s' \
536 % (formatFileMode(oStat.st_mode), oStat.st_nlink, oStat.st_uid, oStat.st_gid, oStat.st_size,
537 time.strftime('%Y-%m-%d %H:%M', time.localtime(oStat.st_mtime)), );
538
539## Good buffer for file operations.
540g_cbGoodBufferSize = 256*1024;
541
542## The original shutil.copyfileobj.
543g_fnOriginalShCopyFileObj = None;
544
545def __myshutilcopyfileobj(fsrc, fdst, length = g_cbGoodBufferSize):
546 """ shutil.copyfileobj with different length default value (16384 is slow with python 2.7 on windows). """
547 return g_fnOriginalShCopyFileObj(fsrc, fdst, length);
548
549def __installShUtilHacks(shutil):
550 """ Installs the shutil buffer size hacks. """
551 global g_fnOriginalShCopyFileObj;
552 if g_fnOriginalShCopyFileObj is None:
553 g_fnOriginalShCopyFileObj = shutil.copyfileobj;
554 shutil.copyfileobj = __myshutilcopyfileobj;
555 return True;
556
557
558def copyFileSimple(sFileSrc, sFileDst):
559 """
560 Wrapper around shutil.copyfile that simply copies the data of a regular file.
561 Raises exception on failure.
562 Return True for show.
563 """
564 import shutil;
565 __installShUtilHacks(shutil);
566 return shutil.copyfile(sFileSrc, sFileDst);
567
568#
569# SubProcess.
570#
571
572def _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs):
573 """
574 If the "executable" is a python script, insert the python interpreter at
575 the head of the argument list so that it will work on systems which doesn't
576 support hash-bang scripts.
577 """
578
579 asArgs = dKeywordArgs.get('args');
580 if asArgs is None:
581 asArgs = aPositionalArgs[0];
582
583 if asArgs[0].endswith('.py'):
584 if sys.executable:
585 asArgs.insert(0, sys.executable);
586 else:
587 asArgs.insert(0, 'python');
588
589 # paranoia...
590 if dKeywordArgs.get('args') is not None:
591 dKeywordArgs['args'] = asArgs;
592 else:
593 aPositionalArgs = (asArgs,) + aPositionalArgs[1:];
594 return None;
595
596def processPopenSafe(*aPositionalArgs, **dKeywordArgs):
597 """
598 Wrapper for subprocess.Popen that's Ctrl-C safe on windows.
599 """
600 if getHostOs() == 'win':
601 if dKeywordArgs.get('creationflags', 0) == 0:
602 dKeywordArgs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP;
603 return subprocess.Popen(*aPositionalArgs, **dKeywordArgs);
604
605def processCall(*aPositionalArgs, **dKeywordArgs):
606 """
607 Wrapper around subprocess.call to deal with its absence in older
608 python versions.
609 Returns process exit code (see subprocess.poll).
610 """
611 assert dKeywordArgs.get('stdout') is None;
612 assert dKeywordArgs.get('stderr') is None;
613 _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
614 oProcess = processPopenSafe(*aPositionalArgs, **dKeywordArgs);
615 return oProcess.wait();
616
617def processOutputChecked(*aPositionalArgs, **dKeywordArgs):
618 """
619 Wrapper around subprocess.check_output to deal with its absense in older
620 python versions.
621
622 Extra keywords for specifying now output is to be decoded:
623 sEncoding='utf-8
624 fIgnoreEncoding=True/False
625 """
626 sEncoding = dKeywordArgs.get('sEncoding');
627 if sEncoding is not None: del dKeywordArgs['sEncoding'];
628 else: sEncoding = 'utf-8';
629
630 fIgnoreEncoding = dKeywordArgs.get('fIgnoreEncoding');
631 if fIgnoreEncoding is not None: del dKeywordArgs['fIgnoreEncoding'];
632 else: fIgnoreEncoding = True;
633
634 _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
635 oProcess = processPopenSafe(stdout=subprocess.PIPE, *aPositionalArgs, **dKeywordArgs);
636
637 sOutput, _ = oProcess.communicate();
638 iExitCode = oProcess.poll();
639
640 if iExitCode is not 0:
641 asArgs = dKeywordArgs.get('args');
642 if asArgs is None:
643 asArgs = aPositionalArgs[0];
644 print(sOutput);
645 raise subprocess.CalledProcessError(iExitCode, asArgs);
646
647 if hasattr(sOutput, 'decode'):
648 sOutput = sOutput.decode(sEncoding, 'ignore' if fIgnoreEncoding else 'strict');
649 return sOutput;
650
651g_fOldSudo = None;
652def _sudoFixArguments(aPositionalArgs, dKeywordArgs, fInitialEnv = True):
653 """
654 Adds 'sudo' (or similar) to the args parameter, whereever it is.
655 """
656
657 # Are we root?
658 fIsRoot = True;
659 try:
660 fIsRoot = os.getuid() == 0; # pylint: disable=E1101
661 except:
662 pass;
663
664 # If not, prepend sudo (non-interactive, simulate initial login).
665 if fIsRoot is not True:
666 asArgs = dKeywordArgs.get('args');
667 if asArgs is None:
668 asArgs = aPositionalArgs[0];
669
670 # Detect old sudo.
671 global g_fOldSudo;
672 if g_fOldSudo is None:
673 try:
674 sVersion = str(processOutputChecked(['sudo', '-V']));
675 except:
676 sVersion = '1.7.0';
677 sVersion = sVersion.strip().split('\n')[0];
678 sVersion = sVersion.replace('Sudo version', '').strip();
679 g_fOldSudo = len(sVersion) >= 4 \
680 and sVersion[0] == '1' \
681 and sVersion[1] == '.' \
682 and sVersion[2] <= '6' \
683 and sVersion[3] == '.';
684
685 asArgs.insert(0, 'sudo');
686 if not g_fOldSudo:
687 asArgs.insert(1, '-n');
688 if fInitialEnv and not g_fOldSudo:
689 asArgs.insert(1, '-i');
690
691 # paranoia...
692 if dKeywordArgs.get('args') is not None:
693 dKeywordArgs['args'] = asArgs;
694 else:
695 aPositionalArgs = (asArgs,) + aPositionalArgs[1:];
696 return None;
697
698
699def sudoProcessCall(*aPositionalArgs, **dKeywordArgs):
700 """
701 sudo (or similar) + subprocess.call
702 """
703 _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
704 _sudoFixArguments(aPositionalArgs, dKeywordArgs);
705 return processCall(*aPositionalArgs, **dKeywordArgs);
706
707def sudoProcessOutputChecked(*aPositionalArgs, **dKeywordArgs):
708 """
709 sudo (or similar) + subprocess.check_output.
710 """
711 _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
712 _sudoFixArguments(aPositionalArgs, dKeywordArgs);
713 return processOutputChecked(*aPositionalArgs, **dKeywordArgs);
714
715def sudoProcessOutputCheckedNoI(*aPositionalArgs, **dKeywordArgs):
716 """
717 sudo (or similar) + subprocess.check_output, except '-i' isn't used.
718 """
719 _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
720 _sudoFixArguments(aPositionalArgs, dKeywordArgs, False);
721 return processOutputChecked(*aPositionalArgs, **dKeywordArgs);
722
723def sudoProcessPopen(*aPositionalArgs, **dKeywordArgs):
724 """
725 sudo (or similar) + processPopenSafe.
726 """
727 _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
728 _sudoFixArguments(aPositionalArgs, dKeywordArgs);
729 return processPopenSafe(*aPositionalArgs, **dKeywordArgs);
730
731
732#
733# Generic process stuff.
734#
735
736def processInterrupt(uPid):
737 """
738 Sends a SIGINT or equivalent to interrupt the specified process.
739 Returns True on success, False on failure.
740
741 On Windows hosts this may not work unless the process happens to be a
742 process group leader.
743 """
744 if sys.platform == 'win32':
745 try:
746 win32console.GenerateConsoleCtrlEvent(win32con.CTRL_BREAK_EVENT, # pylint: disable=no-member,c-extension-no-member
747 uPid);
748 fRc = True;
749 except:
750 fRc = False;
751 else:
752 try:
753 os.kill(uPid, signal.SIGINT);
754 fRc = True;
755 except:
756 fRc = False;
757 return fRc;
758
759def sendUserSignal1(uPid):
760 """
761 Sends a SIGUSR1 or equivalent to nudge the process into shutting down
762 (VBoxSVC) or something.
763 Returns True on success, False on failure or if not supported (win).
764
765 On Windows hosts this may not work unless the process happens to be a
766 process group leader.
767 """
768 if sys.platform == 'win32':
769 fRc = False;
770 else:
771 try:
772 os.kill(uPid, signal.SIGUSR1); # pylint: disable=E1101
773 fRc = True;
774 except:
775 fRc = False;
776 return fRc;
777
778def processTerminate(uPid):
779 """
780 Terminates the process in a nice manner (SIGTERM or equivalent).
781 Returns True on success, False on failure.
782 """
783 fRc = False;
784 if sys.platform == 'win32':
785 try:
786 hProcess = win32api.OpenProcess(win32con.PROCESS_TERMINATE, # pylint: disable=no-member,c-extension-no-member
787 False, uPid);
788 except:
789 pass;
790 else:
791 try:
792 win32process.TerminateProcess(hProcess, # pylint: disable=no-member,c-extension-no-member
793 0x40010004); # DBG_TERMINATE_PROCESS
794 fRc = True;
795 except:
796 pass;
797 hProcess.Close(); #win32api.CloseHandle(hProcess)
798 else:
799 try:
800 os.kill(uPid, signal.SIGTERM);
801 fRc = True;
802 except:
803 pass;
804 return fRc;
805
806def processKill(uPid):
807 """
808 Terminates the process with extreme prejudice (SIGKILL).
809 Returns True on success, False on failure.
810 """
811 if sys.platform == 'win32':
812 fRc = processTerminate(uPid);
813 else:
814 try:
815 os.kill(uPid, signal.SIGKILL); # pylint: disable=E1101
816 fRc = True;
817 except:
818 fRc = False;
819 return fRc;
820
821def processKillWithNameCheck(uPid, sName):
822 """
823 Like processKill(), but checks if the process name matches before killing
824 it. This is intended for killing using potentially stale pid values.
825
826 Returns True on success, False on failure.
827 """
828
829 if processCheckPidAndName(uPid, sName) is not True:
830 return False;
831 return processKill(uPid);
832
833
834def processExists(uPid):
835 """
836 Checks if the specified process exits.
837 This will only work if we can signal/open the process.
838
839 Returns True if it positively exists, False otherwise.
840 """
841 if sys.platform == 'win32':
842 fRc = False;
843 # We try open the process for waiting since this is generally only forbidden in a very few cases.
844 try:
845 hProcess = win32api.OpenProcess(win32con.SYNCHRONIZE, # pylint: disable=no-member,c-extension-no-member
846 False, uPid);
847 except pywintypes.error as oXcpt: # pylint: disable=no-member
848 if oXcpt.winerror == winerror.ERROR_ACCESS_DENIED:
849 fRc = True;
850 except Exception as oXcpt:
851 pass;
852 else:
853 hProcess.Close();
854 fRc = True;
855 else:
856 try:
857 os.kill(uPid, 0);
858 fRc = True;
859 except: ## @todo check error code.
860 fRc = False;
861 return fRc;
862
863def processCheckPidAndName(uPid, sName):
864 """
865 Checks if a process PID and NAME matches.
866 """
867 fRc = processExists(uPid);
868 if fRc is not True:
869 return False;
870
871 if sys.platform == 'win32':
872 try:
873 from win32com.client import GetObject; # pylint: disable=F0401
874 oWmi = GetObject('winmgmts:');
875 aoProcesses = oWmi.InstancesOf('Win32_Process');
876 for oProcess in aoProcesses:
877 if long(oProcess.Properties_("ProcessId").Value) == uPid:
878 sCurName = oProcess.Properties_("Name").Value;
879 #reporter.log2('uPid=%s sName=%s sCurName=%s' % (uPid, sName, sCurName));
880 sName = sName.lower();
881 sCurName = sCurName.lower();
882 if os.path.basename(sName) == sName:
883 sCurName = os.path.basename(sCurName);
884
885 if sCurName == sName \
886 or sCurName + '.exe' == sName \
887 or sCurName == sName + '.exe':
888 fRc = True;
889 break;
890 except:
891 #reporter.logXcpt('uPid=%s sName=%s' % (uPid, sName));
892 pass;
893 else:
894 if sys.platform in ('linux2', 'linux', 'linux3', 'linux4', 'linux5', 'linux6'):
895 asPsCmd = ['/bin/ps', '-p', '%u' % (uPid,), '-o', 'fname='];
896 elif sys.platform in ('sunos5',):
897 asPsCmd = ['/usr/bin/ps', '-p', '%u' % (uPid,), '-o', 'fname='];
898 elif sys.platform in ('darwin',):
899 asPsCmd = ['/bin/ps', '-p', '%u' % (uPid,), '-o', 'ucomm='];
900 else:
901 asPsCmd = None;
902
903 if asPsCmd is not None:
904 try:
905 oPs = subprocess.Popen(asPsCmd, stdout=subprocess.PIPE);
906 sCurName = oPs.communicate()[0];
907 iExitCode = oPs.wait();
908 except:
909 #reporter.logXcpt();
910 return False;
911
912 # ps fails with non-zero exit code if the pid wasn't found.
913 if iExitCode is not 0:
914 return False;
915 if sCurName is None:
916 return False;
917 sCurName = sCurName.strip();
918 if not sCurName:
919 return False;
920
921 if os.path.basename(sName) == sName:
922 sCurName = os.path.basename(sCurName);
923 elif os.path.basename(sCurName) == sCurName:
924 sName = os.path.basename(sName);
925
926 if sCurName != sName:
927 return False;
928
929 fRc = True;
930 return fRc;
931
932def processGetInfo(uPid, fSudo = False):
933 """
934 Tries to acquire state information of the given process.
935
936 Returns a string with the information on success or None on failure or
937 if the host is not supported.
938
939 Note that the format of the information is host system dependent and will
940 likely differ much between different hosts.
941 """
942 fRc = processExists(uPid);
943 if fRc is not True:
944 return None;
945
946 sHostOs = getHostOs();
947 if sHostOs in [ 'linux',]:
948 sGdb = '/usr/bin/gdb';
949 if not os.path.isfile(sGdb): sGdb = '/usr/local/bin/gdb';
950 if not os.path.isfile(sGdb): sGdb = 'gdb';
951 aasCmd = [
952 [ sGdb, '-batch',
953 '-ex', 'set pagination off',
954 '-ex', 'thread apply all bt',
955 '-ex', 'info proc mapping',
956 '-ex', 'info sharedlibrary',
957 '-p', '%u' % (uPid,), ],
958 ];
959 elif sHostOs == 'darwin':
960 # LLDB doesn't work in batch mode when attaching to a process, at least
961 # with macOS Sierra (10.12). GDB might not be installed. Use the sample
962 # tool instead with a 1 second duration and 1000ms sampling interval to
963 # get one stack trace. For the process mappings use vmmap.
964 aasCmd = [
965 [ '/usr/bin/sample', '-mayDie', '%u' % (uPid,), '1', '1000', ],
966 [ '/usr/bin/vmmap', '%u' % (uPid,), ],
967 ];
968 elif sHostOs == 'solaris':
969 aasCmd = [
970 [ '/usr/bin/pstack', '%u' % (uPid,), ],
971 [ '/usr/bin/pmap', '%u' % (uPid,), ],
972 ];
973 else:
974 aasCmd = [];
975
976 sInfo = '';
977 for asCmd in aasCmd:
978 try:
979 if fSudo:
980 sThisInfo = sudoProcessOutputChecked(asCmd);
981 else:
982 sThisInfo = processOutputChecked(asCmd);
983 if sThisInfo is not None:
984 sInfo += sThisInfo;
985 except:
986 pass;
987 if not sInfo:
988 sInfo = None;
989
990 return sInfo;
991
992
993class ProcessInfo(object):
994 """Process info."""
995 def __init__(self, iPid):
996 self.iPid = iPid;
997 self.iParentPid = None;
998 self.sImage = None;
999 self.sName = None;
1000 self.asArgs = None;
1001 self.sCwd = None;
1002 self.iGid = None;
1003 self.iUid = None;
1004 self.iProcGroup = None;
1005 self.iSessionId = None;
1006
1007 def loadAll(self):
1008 """Load all the info."""
1009 sOs = getHostOs();
1010 if sOs == 'linux':
1011 sProc = '/proc/%s/' % (self.iPid,);
1012 if self.sImage is None: self.sImage = noxcptReadLink(sProc + 'exe', None);
1013 if self.sCwd is None: self.sCwd = noxcptReadLink(sProc + 'cwd', None);
1014 if self.asArgs is None: self.asArgs = noxcptReadFile(sProc + 'cmdline', '').split('\x00');
1015 #elif sOs == 'solaris': - doesn't work for root processes, suid proces, and other stuff.
1016 # sProc = '/proc/%s/' % (self.iPid,);
1017 # if self.sImage is None: self.sImage = noxcptReadLink(sProc + 'path/a.out', None);
1018 # if self.sCwd is None: self.sCwd = noxcptReadLink(sProc + 'path/cwd', None);
1019 else:
1020 pass;
1021 if self.sName is None and self.sImage is not None:
1022 self.sName = self.sImage;
1023
1024 def windowsGrabProcessInfo(self, oProcess):
1025 """Windows specific loadAll."""
1026 try: self.sName = oProcess.Properties_("Name").Value;
1027 except: pass;
1028 try: self.sImage = oProcess.Properties_("ExecutablePath").Value;
1029 except: pass;
1030 try: self.asArgs = [oProcess.Properties_("CommandLine").Value]; ## @todo split it.
1031 except: pass;
1032 try: self.iParentPid = oProcess.Properties_("ParentProcessId").Value;
1033 except: pass;
1034 try: self.iSessionId = oProcess.Properties_("SessionId").Value;
1035 except: pass;
1036 if self.sName is None and self.sImage is not None:
1037 self.sName = self.sImage;
1038
1039 def getBaseImageName(self):
1040 """
1041 Gets the base image name if available, use the process name if not available.
1042 Returns image/process base name or None.
1043 """
1044 sRet = self.sImage if self.sName is None else self.sName;
1045 if sRet is None:
1046 self.loadAll();
1047 sRet = self.sImage if self.sName is None else self.sName;
1048 if sRet is None:
1049 if not self.asArgs:
1050 return None;
1051 sRet = self.asArgs[0];
1052 if not sRet:
1053 return None;
1054 return os.path.basename(sRet);
1055
1056 def getBaseImageNameNoExeSuff(self):
1057 """
1058 Same as getBaseImageName, except any '.exe' or similar suffix is stripped.
1059 """
1060 sRet = self.getBaseImageName();
1061 if sRet is not None and len(sRet) > 4 and sRet[-4] == '.':
1062 if (sRet[-4:]).lower() in [ '.exe', '.com', '.msc', '.vbs', '.cmd', '.bat' ]:
1063 sRet = sRet[:-4];
1064 return sRet;
1065
1066
1067def processListAll(): # pylint: disable=R0914
1068 """
1069 Return a list of ProcessInfo objects for all the processes in the system
1070 that the current user can see.
1071 """
1072 asProcesses = [];
1073
1074 sOs = getHostOs();
1075 if sOs == 'win':
1076 from win32com.client import GetObject; # pylint: disable=F0401
1077 oWmi = GetObject('winmgmts:');
1078 aoProcesses = oWmi.InstancesOf('Win32_Process');
1079 for oProcess in aoProcesses:
1080 try:
1081 iPid = int(oProcess.Properties_("ProcessId").Value);
1082 except:
1083 continue;
1084 oMyInfo = ProcessInfo(iPid);
1085 oMyInfo.windowsGrabProcessInfo(oProcess);
1086 asProcesses.append(oMyInfo);
1087 return asProcesses;
1088
1089 if sOs in [ 'linux', ]: # Not solaris, ps gets more info than /proc/.
1090 try:
1091 asDirs = os.listdir('/proc');
1092 except:
1093 asDirs = [];
1094 for sDir in asDirs:
1095 if sDir.isdigit():
1096 asProcesses.append(ProcessInfo(int(sDir),));
1097 return asProcesses;
1098
1099 #
1100 # The other OSes parses the output from the 'ps' utility.
1101 #
1102 asPsCmd = [
1103 '/bin/ps', # 0
1104 '-A', # 1
1105 '-o', 'pid=', # 2,3
1106 '-o', 'ppid=', # 4,5
1107 '-o', 'pgid=', # 6,7
1108 '-o', 'sid=', # 8,9
1109 '-o', 'uid=', # 10,11
1110 '-o', 'gid=', # 12,13
1111 '-o', 'comm=' # 14,15
1112 ];
1113
1114 if sOs == 'darwin':
1115 assert asPsCmd[9] == 'sid=';
1116 asPsCmd[9] = 'sess=';
1117 elif sOs == 'solaris':
1118 asPsCmd[0] = '/usr/bin/ps';
1119
1120 try:
1121 sRaw = processOutputChecked(asPsCmd);
1122 except:
1123 return asProcesses;
1124
1125 for sLine in sRaw.split('\n'):
1126 sLine = sLine.lstrip();
1127 if len(sLine) < 7 or not sLine[0].isdigit():
1128 continue;
1129
1130 iField = 0;
1131 off = 0;
1132 aoFields = [None, None, None, None, None, None, None];
1133 while iField < 7:
1134 # Eat whitespace.
1135 while off < len(sLine) and (sLine[off] == ' ' or sLine[off] == '\t'):
1136 off += 1;
1137
1138 # Final field / EOL.
1139 if iField == 6:
1140 aoFields[6] = sLine[off:];
1141 break;
1142 if off >= len(sLine):
1143 break;
1144
1145 # Generic field parsing.
1146 offStart = off;
1147 off += 1;
1148 while off < len(sLine) and sLine[off] != ' ' and sLine[off] != '\t':
1149 off += 1;
1150 try:
1151 if iField != 3:
1152 aoFields[iField] = int(sLine[offStart:off]);
1153 else:
1154 aoFields[iField] = long(sLine[offStart:off], 16); # sess is a hex address.
1155 except:
1156 pass;
1157 iField += 1;
1158
1159 if aoFields[0] is not None:
1160 oMyInfo = ProcessInfo(aoFields[0]);
1161 oMyInfo.iParentPid = aoFields[1];
1162 oMyInfo.iProcGroup = aoFields[2];
1163 oMyInfo.iSessionId = aoFields[3];
1164 oMyInfo.iUid = aoFields[4];
1165 oMyInfo.iGid = aoFields[5];
1166 oMyInfo.sName = aoFields[6];
1167 asProcesses.append(oMyInfo);
1168
1169 return asProcesses;
1170
1171
1172def processCollectCrashInfo(uPid, fnLog, fnCrashFile):
1173 """
1174 Looks for information regarding the demise of the given process.
1175 """
1176 sOs = getHostOs();
1177 if sOs == 'darwin':
1178 #
1179 # On darwin we look for crash and diagnostic reports.
1180 #
1181 asLogDirs = [
1182 u'/Library/Logs/DiagnosticReports/',
1183 u'/Library/Logs/CrashReporter/',
1184 u'~/Library/Logs/DiagnosticReports/',
1185 u'~/Library/Logs/CrashReporter/',
1186 ];
1187 for sDir in asLogDirs:
1188 sDir = os.path.expanduser(sDir);
1189 if not os.path.isdir(sDir):
1190 continue;
1191 try:
1192 asDirEntries = os.listdir(sDir);
1193 except:
1194 continue;
1195 for sEntry in asDirEntries:
1196 # Only interested in .crash files.
1197 _, sSuff = os.path.splitext(sEntry);
1198 if sSuff != '.crash':
1199 continue;
1200
1201 # The pid can be found at the end of the first line.
1202 sFull = os.path.join(sDir, sEntry);
1203 try:
1204 oFile = open(sFull, 'r');
1205 sFirstLine = oFile.readline();
1206 oFile.close();
1207 except:
1208 continue;
1209 if len(sFirstLine) <= 4 or sFirstLine[-2] != ']':
1210 continue;
1211 offPid = len(sFirstLine) - 3;
1212 while offPid > 1 and sFirstLine[offPid - 1].isdigit():
1213 offPid -= 1;
1214 try: uReportPid = int(sFirstLine[offPid:-2]);
1215 except: continue;
1216
1217 # Does the pid we found match?
1218 if uReportPid == uPid:
1219 fnLog('Found crash report for %u: %s' % (uPid, sFull,));
1220 fnCrashFile(sFull, False);
1221 elif sOs == 'win':
1222 #
1223 # Getting WER reports would be great, however we have trouble match the
1224 # PID to those as they seems not to mention it in the brief reports.
1225 # Instead we'll just look for crash dumps in C:\CrashDumps (our custom
1226 # location - see the windows readme for the testbox script) and what
1227 # the MSDN article lists for now.
1228 #
1229 # It's been observed on Windows server 2012 that the dump files takes
1230 # the form: <processimage>.<decimal-pid>.dmp
1231 #
1232 asDmpDirs = [
1233 u'%SystemDrive%/CrashDumps/', # Testboxes.
1234 u'%LOCALAPPDATA%/CrashDumps/', # MSDN example.
1235 u'%WINDIR%/ServiceProfiles/LocalServices/', # Local and network service.
1236 u'%WINDIR%/ServiceProfiles/NetworkSerices/',
1237 u'%WINDIR%/ServiceProfiles/',
1238 u'%WINDIR%/System32/Config/SystemProfile/', # System services.
1239 ];
1240 sMatchSuffix = '.%u.dmp' % (uPid,);
1241
1242 for sDir in asDmpDirs:
1243 sDir = os.path.expandvars(sDir);
1244 if not os.path.isdir(sDir):
1245 continue;
1246 try:
1247 asDirEntries = os.listdir(sDir);
1248 except:
1249 continue;
1250 for sEntry in asDirEntries:
1251 if sEntry.endswith(sMatchSuffix):
1252 sFull = os.path.join(sDir, sEntry);
1253 fnLog('Found crash dump for %u: %s' % (uPid, sFull,));
1254 fnCrashFile(sFull, True);
1255
1256 else:
1257 pass; ## TODO
1258 return None;
1259
1260
1261#
1262# Time.
1263#
1264
1265#
1266# The following test case shows how time.time() only have ~ms resolution
1267# on Windows (tested W10) and why it therefore makes sense to try use
1268# performance counters.
1269#
1270# Note! We cannot use time.clock() as the timestamp must be portable across
1271# processes. See timeout testcase problem on win hosts (no logs).
1272#
1273#import sys;
1274#import time;
1275#from common import utils;
1276#
1277#atSeries = [];
1278#for i in xrange(1,160):
1279# if i == 159: time.sleep(10);
1280# atSeries.append((utils.timestampNano(), long(time.clock() * 1000000000), long(time.time() * 1000000000)));
1281#
1282#tPrev = atSeries[0]
1283#for tCur in atSeries:
1284# print 't1=%+22u, %u' % (tCur[0], tCur[0] - tPrev[0]);
1285# print 't2=%+22u, %u' % (tCur[1], tCur[1] - tPrev[1]);
1286# print 't3=%+22u, %u' % (tCur[2], tCur[2] - tPrev[2]);
1287# print '';
1288# tPrev = tCur
1289#
1290#print 't1=%u' % (atSeries[-1][0] - atSeries[0][0]);
1291#print 't2=%u' % (atSeries[-1][1] - atSeries[0][1]);
1292#print 't3=%u' % (atSeries[-1][2] - atSeries[0][2]);
1293
1294g_fWinUseWinPerfCounter = sys.platform == 'win32';
1295g_fpWinPerfCounterFreq = None;
1296g_oFuncwinQueryPerformanceCounter = None;
1297
1298def _winInitPerfCounter():
1299 """ Initializes the use of performance counters. """
1300 global g_fWinUseWinPerfCounter, g_fpWinPerfCounterFreq, g_oFuncwinQueryPerformanceCounter
1301
1302 uFrequency = ctypes.c_ulonglong(0);
1303 if ctypes.windll.kernel32.QueryPerformanceFrequency(ctypes.byref(uFrequency)):
1304 if uFrequency.value >= 1000:
1305 #print 'uFrequency = %s' % (uFrequency,);
1306 #print 'type(uFrequency) = %s' % (type(uFrequency),);
1307 g_fpWinPerfCounterFreq = float(uFrequency.value);
1308
1309 # Check that querying the counter works too.
1310 global g_oFuncwinQueryPerformanceCounter
1311 g_oFuncwinQueryPerformanceCounter = ctypes.windll.kernel32.QueryPerformanceCounter;
1312 uCurValue = ctypes.c_ulonglong(0);
1313 if g_oFuncwinQueryPerformanceCounter(ctypes.byref(uCurValue)):
1314 if uCurValue.value > 0:
1315 return True;
1316 g_fWinUseWinPerfCounter = False;
1317 return False;
1318
1319def _winFloatTime():
1320 """ Gets floating point time on windows. """
1321 if g_fpWinPerfCounterFreq is not None or _winInitPerfCounter():
1322 uCurValue = ctypes.c_ulonglong(0);
1323 if g_oFuncwinQueryPerformanceCounter(ctypes.byref(uCurValue)):
1324 return float(uCurValue.value) / g_fpWinPerfCounterFreq;
1325 return time.time();
1326
1327
1328def timestampNano():
1329 """
1330 Gets a nanosecond timestamp.
1331 """
1332 if g_fWinUseWinPerfCounter is True:
1333 return long(_winFloatTime() * 1000000000);
1334 return long(time.time() * 1000000000);
1335
1336def timestampMilli():
1337 """
1338 Gets a millisecond timestamp.
1339 """
1340 if g_fWinUseWinPerfCounter is True:
1341 return long(_winFloatTime() * 1000);
1342 return long(time.time() * 1000);
1343
1344def timestampSecond():
1345 """
1346 Gets a second timestamp.
1347 """
1348 if g_fWinUseWinPerfCounter is True:
1349 return long(_winFloatTime());
1350 return long(time.time());
1351
1352def getTimePrefix():
1353 """
1354 Returns a timestamp prefix, typically used for logging. UTC.
1355 """
1356 try:
1357 oNow = datetime.datetime.utcnow();
1358 sTs = '%02u:%02u:%02u.%06u' % (oNow.hour, oNow.minute, oNow.second, oNow.microsecond);
1359 except:
1360 sTs = 'getTimePrefix-exception';
1361 return sTs;
1362
1363def getTimePrefixAndIsoTimestamp():
1364 """
1365 Returns current UTC as log prefix and iso timestamp.
1366 """
1367 try:
1368 oNow = datetime.datetime.utcnow();
1369 sTsPrf = '%02u:%02u:%02u.%06u' % (oNow.hour, oNow.minute, oNow.second, oNow.microsecond);
1370 sTsIso = formatIsoTimestamp(oNow);
1371 except:
1372 sTsPrf = sTsIso = 'getTimePrefix-exception';
1373 return (sTsPrf, sTsIso);
1374
1375def formatIsoTimestamp(oNow):
1376 """Formats the datetime object as an ISO timestamp."""
1377 assert oNow.tzinfo is None;
1378 sTs = '%s.%09uZ' % (oNow.strftime('%Y-%m-%dT%H:%M:%S'), oNow.microsecond * 1000);
1379 return sTs;
1380
1381def getIsoTimestamp():
1382 """Returns the current UTC timestamp as a string."""
1383 return formatIsoTimestamp(datetime.datetime.utcnow());
1384
1385
1386def getLocalHourOfWeek():
1387 """ Local hour of week (0 based). """
1388 oNow = datetime.datetime.now();
1389 return (oNow.isoweekday() - 1) * 24 + oNow.hour;
1390
1391
1392def formatIntervalSeconds(cSeconds):
1393 """ Format a seconds interval into a nice 01h 00m 22s string """
1394 # Two simple special cases.
1395 if cSeconds < 60:
1396 return '%ss' % (cSeconds,);
1397 if cSeconds < 3600:
1398 cMins = cSeconds / 60;
1399 cSecs = cSeconds % 60;
1400 if cSecs == 0:
1401 return '%sm' % (cMins,);
1402 return '%sm %ss' % (cMins, cSecs,);
1403
1404 # Generic and a bit slower.
1405 cDays = cSeconds / 86400;
1406 cSeconds %= 86400;
1407 cHours = cSeconds / 3600;
1408 cSeconds %= 3600;
1409 cMins = cSeconds / 60;
1410 cSecs = cSeconds % 60;
1411 sRet = '';
1412 if cDays > 0:
1413 sRet = '%sd ' % (cDays,);
1414 if cHours > 0:
1415 sRet += '%sh ' % (cHours,);
1416 if cMins > 0:
1417 sRet += '%sm ' % (cMins,);
1418 if cSecs > 0:
1419 sRet += '%ss ' % (cSecs,);
1420 assert sRet; assert sRet[-1] == ' ';
1421 return sRet[:-1];
1422
1423def formatIntervalSeconds2(oSeconds):
1424 """
1425 Flexible input version of formatIntervalSeconds for use in WUI forms where
1426 data is usually already string form.
1427 """
1428 if isinstance(oSeconds, (int, long)):
1429 return formatIntervalSeconds(oSeconds);
1430 if not isString(oSeconds):
1431 try:
1432 lSeconds = long(oSeconds);
1433 except:
1434 pass;
1435 else:
1436 if lSeconds >= 0:
1437 return formatIntervalSeconds2(lSeconds);
1438 return oSeconds;
1439
1440def parseIntervalSeconds(sString):
1441 """
1442 Reverse of formatIntervalSeconds.
1443
1444 Returns (cSeconds, sError), where sError is None on success.
1445 """
1446
1447 # We might given non-strings, just return them without any fuss.
1448 if not isString(sString):
1449 if isinstance(sString, (int, long)) or sString is None:
1450 return (sString, None);
1451 ## @todo time/date objects?
1452 return (int(sString), None);
1453
1454 # Strip it and make sure it's not empty.
1455 sString = sString.strip();
1456 if not sString:
1457 return (0, 'Empty interval string.');
1458
1459 #
1460 # Split up the input into a list of 'valueN, unitN, ...'.
1461 #
1462 # Don't want to spend too much time trying to make re.split do exactly what
1463 # I need here, so please forgive the extra pass I'm making here.
1464 #
1465 asRawParts = re.split(r'\s*([0-9]+)\s*([^0-9,;]*)[\s,;]*', sString);
1466 asParts = [];
1467 for sPart in asRawParts:
1468 sPart = sPart.strip();
1469 if sPart:
1470 asParts.append(sPart);
1471 if not asParts:
1472 return (0, 'Empty interval string or something?');
1473
1474 #
1475 # Process them one or two at the time.
1476 #
1477 cSeconds = 0;
1478 asErrors = [];
1479 i = 0;
1480 while i < len(asParts):
1481 sNumber = asParts[i];
1482 i += 1;
1483 if sNumber.isdigit():
1484 iNumber = int(sNumber);
1485
1486 sUnit = 's';
1487 if i < len(asParts) and not asParts[i].isdigit():
1488 sUnit = asParts[i];
1489 i += 1;
1490
1491 sUnitLower = sUnit.lower();
1492 if sUnitLower in [ 's', 'se', 'sec', 'second', 'seconds' ]:
1493 pass;
1494 elif sUnitLower in [ 'm', 'mi', 'min', 'minute', 'minutes' ]:
1495 iNumber *= 60;
1496 elif sUnitLower in [ 'h', 'ho', 'hou', 'hour', 'hours' ]:
1497 iNumber *= 3600;
1498 elif sUnitLower in [ 'd', 'da', 'day', 'days' ]:
1499 iNumber *= 86400;
1500 elif sUnitLower in [ 'w', 'week', 'weeks' ]:
1501 iNumber *= 7 * 86400;
1502 else:
1503 asErrors.append('Unknown unit "%s".' % (sUnit,));
1504 cSeconds += iNumber;
1505 else:
1506 asErrors.append('Bad number "%s".' % (sNumber,));
1507 return (cSeconds, None if not asErrors else ' '.join(asErrors));
1508
1509def formatIntervalHours(cHours):
1510 """ Format a hours interval into a nice 1w 2d 1h string. """
1511 # Simple special cases.
1512 if cHours < 24:
1513 return '%sh' % (cHours,);
1514
1515 # Generic and a bit slower.
1516 cWeeks = cHours / (7 * 24);
1517 cHours %= 7 * 24;
1518 cDays = cHours / 24;
1519 cHours %= 24;
1520 sRet = '';
1521 if cWeeks > 0:
1522 sRet = '%sw ' % (cWeeks,);
1523 if cDays > 0:
1524 sRet = '%sd ' % (cDays,);
1525 if cHours > 0:
1526 sRet += '%sh ' % (cHours,);
1527 assert sRet; assert sRet[-1] == ' ';
1528 return sRet[:-1];
1529
1530def parseIntervalHours(sString):
1531 """
1532 Reverse of formatIntervalHours.
1533
1534 Returns (cHours, sError), where sError is None on success.
1535 """
1536
1537 # We might given non-strings, just return them without any fuss.
1538 if not isString(sString):
1539 if isinstance(sString, (int, long)) or sString is None:
1540 return (sString, None);
1541 ## @todo time/date objects?
1542 return (int(sString), None);
1543
1544 # Strip it and make sure it's not empty.
1545 sString = sString.strip();
1546 if not sString:
1547 return (0, 'Empty interval string.');
1548
1549 #
1550 # Split up the input into a list of 'valueN, unitN, ...'.
1551 #
1552 # Don't want to spend too much time trying to make re.split do exactly what
1553 # I need here, so please forgive the extra pass I'm making here.
1554 #
1555 asRawParts = re.split(r'\s*([0-9]+)\s*([^0-9,;]*)[\s,;]*', sString);
1556 asParts = [];
1557 for sPart in asRawParts:
1558 sPart = sPart.strip();
1559 if sPart:
1560 asParts.append(sPart);
1561 if not asParts:
1562 return (0, 'Empty interval string or something?');
1563
1564 #
1565 # Process them one or two at the time.
1566 #
1567 cHours = 0;
1568 asErrors = [];
1569 i = 0;
1570 while i < len(asParts):
1571 sNumber = asParts[i];
1572 i += 1;
1573 if sNumber.isdigit():
1574 iNumber = int(sNumber);
1575
1576 sUnit = 'h';
1577 if i < len(asParts) and not asParts[i].isdigit():
1578 sUnit = asParts[i];
1579 i += 1;
1580
1581 sUnitLower = sUnit.lower();
1582 if sUnitLower in [ 'h', 'ho', 'hou', 'hour', 'hours' ]:
1583 pass;
1584 elif sUnitLower in [ 'd', 'da', 'day', 'days' ]:
1585 iNumber *= 24;
1586 elif sUnitLower in [ 'w', 'week', 'weeks' ]:
1587 iNumber *= 7 * 24;
1588 else:
1589 asErrors.append('Unknown unit "%s".' % (sUnit,));
1590 cHours += iNumber;
1591 else:
1592 asErrors.append('Bad number "%s".' % (sNumber,));
1593 return (cHours, None if not asErrors else ' '.join(asErrors));
1594
1595
1596#
1597# Introspection.
1598#
1599
1600def getCallerName(oFrame=None, iFrame=2):
1601 """
1602 Returns the name of the caller's caller.
1603 """
1604 if oFrame is None:
1605 try:
1606 raise Exception();
1607 except:
1608 oFrame = sys.exc_info()[2].tb_frame.f_back;
1609 while iFrame > 1:
1610 if oFrame is not None:
1611 oFrame = oFrame.f_back;
1612 iFrame = iFrame - 1;
1613 if oFrame is not None:
1614 sName = '%s:%u' % (oFrame.f_code.co_name, oFrame.f_lineno);
1615 return sName;
1616 return "unknown";
1617
1618
1619def getXcptInfo(cFrames = 1):
1620 """
1621 Gets text detailing the exception. (Good for logging.)
1622 Returns list of info strings.
1623 """
1624
1625 #
1626 # Try get exception info.
1627 #
1628 try:
1629 oType, oValue, oTraceback = sys.exc_info();
1630 except:
1631 oType = oValue = oTraceback = None;
1632 if oType is not None:
1633
1634 #
1635 # Try format the info
1636 #
1637 asRet = [];
1638 try:
1639 try:
1640 asRet = asRet + traceback.format_exception_only(oType, oValue);
1641 asTraceBack = traceback.format_tb(oTraceback);
1642 if cFrames is not None and cFrames <= 1:
1643 asRet.append(asTraceBack[-1]);
1644 else:
1645 asRet.append('Traceback:')
1646 for iFrame in range(min(cFrames, len(asTraceBack))):
1647 asRet.append(asTraceBack[-iFrame - 1]);
1648 asRet.append('Stack:')
1649 asRet = asRet + traceback.format_stack(oTraceback.tb_frame.f_back, cFrames);
1650 except:
1651 asRet.append('internal-error: Hit exception #2! %s' % (traceback.format_exc(),));
1652
1653 if not asRet:
1654 asRet.append('No exception info...');
1655 except:
1656 asRet.append('internal-error: Hit exception! %s' % (traceback.format_exc(),));
1657 else:
1658 asRet = ['Couldn\'t find exception traceback.'];
1659 return asRet;
1660
1661
1662def getObjectTypeName(oObject):
1663 """
1664 Get the type name of the given object.
1665 """
1666 if oObject is None:
1667 return 'None';
1668
1669 # Get the type object.
1670 try:
1671 oType = type(oObject);
1672 except:
1673 return 'type-throws-exception';
1674
1675 # Python 2.x only: Handle old-style object wrappers.
1676 if sys.version_info[0] < 3:
1677 try:
1678 from types import InstanceType; # pylint: disable=no-name-in-module
1679 if oType == InstanceType:
1680 oType = oObject.__class__;
1681 except:
1682 pass;
1683
1684 # Get the name.
1685 try:
1686 return oType.__name__;
1687 except:
1688 return '__type__-throws-exception';
1689
1690
1691#
1692# TestSuite stuff.
1693#
1694
1695def isRunningFromCheckout(cScriptDepth = 1):
1696 """
1697 Checks if we're running from the SVN checkout or not.
1698 """
1699
1700 try:
1701 sFile = __file__;
1702 cScriptDepth = 1;
1703 except:
1704 sFile = sys.argv[0];
1705
1706 sDir = os.path.abspath(sFile);
1707 while cScriptDepth >= 0:
1708 sDir = os.path.dirname(sDir);
1709 if os.path.exists(os.path.join(sDir, 'Makefile.kmk')) \
1710 or os.path.exists(os.path.join(sDir, 'Makefile.kup')):
1711 return True;
1712 cScriptDepth -= 1;
1713
1714 return False;
1715
1716
1717#
1718# Bourne shell argument fun.
1719#
1720
1721
1722def argsSplit(sCmdLine):
1723 """
1724 Given a bourne shell command line invocation, split it up into arguments
1725 assuming IFS is space.
1726 Returns None on syntax error.
1727 """
1728 ## @todo bourne shell argument parsing!
1729 return sCmdLine.split(' ');
1730
1731def argsGetFirst(sCmdLine):
1732 """
1733 Given a bourne shell command line invocation, get return the first argument
1734 assuming IFS is space.
1735 Returns None on invalid syntax, otherwise the parsed and unescaped argv[0] string.
1736 """
1737 asArgs = argsSplit(sCmdLine);
1738 if not asArgs:
1739 return None;
1740
1741 return asArgs[0];
1742
1743#
1744# String helpers.
1745#
1746
1747def stricmp(sFirst, sSecond):
1748 """
1749 Compares to strings in an case insensitive fashion.
1750
1751 Python doesn't seem to have any way of doing the correctly, so this is just
1752 an approximation using lower.
1753 """
1754 if sFirst == sSecond:
1755 return 0;
1756 sLower1 = sFirst.lower();
1757 sLower2 = sSecond.lower();
1758 if sLower1 == sLower2:
1759 return 0;
1760 if sLower1 < sLower2:
1761 return -1;
1762 return 1;
1763
1764
1765#
1766# Misc.
1767#
1768
1769def versionCompare(sVer1, sVer2):
1770 """
1771 Compares to version strings in a fashion similar to RTStrVersionCompare.
1772 """
1773
1774 ## @todo implement me!!
1775
1776 if sVer1 == sVer2:
1777 return 0;
1778 if sVer1 < sVer2:
1779 return -1;
1780 return 1;
1781
1782
1783def formatNumber(lNum, sThousandSep = ' '):
1784 """
1785 Formats a decimal number with pretty separators.
1786 """
1787 sNum = str(lNum);
1788 sRet = sNum[-3:];
1789 off = len(sNum) - 3;
1790 while off > 0:
1791 off -= 3;
1792 sRet = sNum[(off if off >= 0 else 0):(off + 3)] + sThousandSep + sRet;
1793 return sRet;
1794
1795
1796def formatNumberNbsp(lNum):
1797 """
1798 Formats a decimal number with pretty separators.
1799 """
1800 sRet = formatNumber(lNum);
1801 return unicode(sRet).replace(' ', u'\u00a0');
1802
1803
1804def isString(oString):
1805 """
1806 Checks if the object is a string object, hiding difference between python 2 and 3.
1807
1808 Returns True if it's a string of some kind.
1809 Returns False if not.
1810 """
1811 if sys.version_info[0] >= 3:
1812 return isinstance(oString, str);
1813 return isinstance(oString, basestring); # pylint: disable=undefined-variable
1814
1815
1816def hasNonAsciiCharacters(sText):
1817 """
1818 Returns True is specified string has non-ASCII characters, False if ASCII only.
1819 """
1820 sTmp = unicode(sText, errors='ignore') if isinstance(sText, str) else sText;
1821 return not all(ord(ch) < 128 for ch in sTmp);
1822
1823
1824def chmodPlusX(sFile):
1825 """
1826 Makes the specified file or directory executable.
1827 Returns success indicator, no exceptions.
1828
1829 Note! Symbolic links are followed and the target will be changed.
1830 """
1831 try:
1832 oStat = os.stat(sFile);
1833 except:
1834 return False;
1835 try:
1836 os.chmod(sFile, oStat.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH);
1837 except:
1838 return False;
1839 return True;
1840
1841
1842def unpackZipFile(sArchive, sDstDir, fnLog, fnError = None, fnFilter = None):
1843 # type: (string, string, (string) -> None, (string) -> None, (string) -> bool) -> list[string]
1844 """
1845 Worker for unpackFile that deals with ZIP files, same function signature.
1846 """
1847 import zipfile
1848 if fnError is None:
1849 fnError = fnLog;
1850
1851 fnLog('Unzipping "%s" to "%s"...' % (sArchive, sDstDir));
1852
1853 # Open it.
1854 try: oZipFile = zipfile.ZipFile(sArchive, 'r')
1855 except Exception as oXcpt:
1856 fnError('Error opening "%s" for unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt,));
1857 return None;
1858
1859 # Extract all members.
1860 asMembers = [];
1861 try:
1862 for sMember in oZipFile.namelist():
1863 if fnFilter is None or fnFilter(sMember) is not False:
1864 if sMember.endswith('/'):
1865 os.makedirs(os.path.join(sDstDir, sMember.replace('/', os.path.sep)), 0x1fd); # octal: 0775 (python 3/2)
1866 else:
1867 oZipFile.extract(sMember, sDstDir);
1868 asMembers.append(os.path.join(sDstDir, sMember.replace('/', os.path.sep)));
1869 except Exception as oXcpt:
1870 fnError('Error unpacking "%s" into "%s": %s' % (sArchive, sDstDir, oXcpt));
1871 asMembers = None;
1872
1873 # close it.
1874 try: oZipFile.close();
1875 except Exception as oXcpt:
1876 fnError('Error closing "%s" after unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt));
1877 asMembers = None;
1878
1879 return asMembers;
1880
1881
1882## Set if we've replaced tarfile.copyfileobj with __mytarfilecopyfileobj already.
1883g_fTarCopyFileObjOverriddend = False;
1884
1885def __mytarfilecopyfileobj(src, dst, length = None, exception = OSError, bufsize = None):
1886 """ tarfile.copyfileobj with different buffer size (16384 is slow on windows). """
1887 _ = bufsize;
1888 if length is None:
1889 __myshutilcopyfileobj(src, dst, g_cbGoodBufferSize);
1890 elif length > 0:
1891 cFull, cbRemainder = divmod(length, g_cbGoodBufferSize);
1892 for _ in xrange(cFull):
1893 abBuffer = src.read(g_cbGoodBufferSize);
1894 dst.write(abBuffer);
1895 if len(abBuffer) != g_cbGoodBufferSize:
1896 raise exception('unexpected end of source file');
1897 if cbRemainder > 0:
1898 abBuffer = src.read(cbRemainder);
1899 dst.write(abBuffer);
1900 if len(abBuffer) != cbRemainder:
1901 raise exception('unexpected end of source file');
1902
1903
1904def unpackTarFile(sArchive, sDstDir, fnLog, fnError = None, fnFilter = None):
1905 # type: (string, string, (string) -> None, (string) -> None, (string) -> bool) -> list[string]
1906 """
1907 Worker for unpackFile that deals with tarballs, same function signature.
1908 """
1909 import shutil;
1910 import tarfile;
1911 if fnError is None:
1912 fnError = fnLog;
1913
1914 fnLog('Untarring "%s" to "%s"...' % (sArchive, sDstDir));
1915
1916 #
1917 # Default buffer sizes of 16384 bytes is causing too many syscalls on Windows.
1918 # 60%+ speedup for python 2.7 and 50%+ speedup for python 3.5, both on windows with PDBs.
1919 # 20%+ speedup for python 2.7 and 15%+ speedup for python 3.5, both on windows skipping PDBs.
1920 #
1921 if True is True:
1922 __installShUtilHacks(shutil);
1923 global g_fTarCopyFileObjOverriddend;
1924 if g_fTarCopyFileObjOverriddend is False:
1925 g_fTarCopyFileObjOverriddend = True;
1926 #if sys.hexversion < 0x03060000:
1927 tarfile.copyfileobj = __mytarfilecopyfileobj;
1928
1929 #
1930 # Open it.
1931 #
1932 # Note! We not using 'r:*' because we cannot allow seeking compressed files!
1933 # That's how we got a 13 min unpack time for VBoxAll on windows (hardlinked pdb).
1934 #
1935 try:
1936 if sys.hexversion >= 0x03060000:
1937 oTarFile = tarfile.open(sArchive, 'r|*', bufsize = g_cbGoodBufferSize, copybufsize = g_cbGoodBufferSize);
1938 else:
1939 oTarFile = tarfile.open(sArchive, 'r|*', bufsize = g_cbGoodBufferSize);
1940 except Exception as oXcpt:
1941 fnError('Error opening "%s" for unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt,));
1942 return None;
1943
1944 # Extract all members.
1945 asMembers = [];
1946 try:
1947 for oTarInfo in oTarFile:
1948 try:
1949 if fnFilter is None or fnFilter(oTarInfo.name) is not False:
1950 if oTarInfo.islnk():
1951 # Links are trouble, especially on Windows. We must avoid the falling that will end up seeking
1952 # in the compressed tar stream. So, fall back on shutil.copy2 instead.
1953 sLinkFile = os.path.join(sDstDir, oTarInfo.name.rstrip('/').replace('/', os.path.sep));
1954 sLinkTarget = os.path.join(sDstDir, oTarInfo.linkname.rstrip('/').replace('/', os.path.sep));
1955 sParentDir = os.path.dirname(sLinkFile);
1956 try: os.unlink(sLinkFile);
1957 except: pass;
1958 if sParentDir and not os.path.exists(sParentDir):
1959 os.makedirs(sParentDir);
1960 try: os.link(sLinkTarget, sLinkFile);
1961 except: shutil.copy2(sLinkTarget, sLinkFile);
1962 else:
1963 if oTarInfo.isdir():
1964 # Just make sure the user (we) got full access to dirs. Don't bother getting it 100% right.
1965 oTarInfo.mode |= 0x1c0; # (octal: 0700)
1966 oTarFile.extract(oTarInfo, sDstDir);
1967 asMembers.append(os.path.join(sDstDir, oTarInfo.name.replace('/', os.path.sep)));
1968 except Exception as oXcpt:
1969 fnError('Error unpacking "%s" member "%s" into "%s": %s' % (sArchive, oTarInfo.name, sDstDir, oXcpt));
1970 for sAttr in [ 'name', 'linkname', 'type', 'mode', 'size', 'mtime', 'uid', 'uname', 'gid', 'gname' ]:
1971 fnError('Info: %8s=%s' % (sAttr, getattr(oTarInfo, sAttr),));
1972 for sFn in [ 'isdir', 'isfile', 'islnk', 'issym' ]:
1973 fnError('Info: %8s=%s' % (sFn, getattr(oTarInfo, sFn)(),));
1974 asMembers = None;
1975 break;
1976 except Exception as oXcpt:
1977 fnError('Error unpacking "%s" into "%s": %s' % (sArchive, sDstDir, oXcpt));
1978 asMembers = None;
1979
1980 #
1981 # Finally, close it.
1982 #
1983 try: oTarFile.close();
1984 except Exception as oXcpt:
1985 fnError('Error closing "%s" after unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt));
1986 asMembers = None;
1987
1988 return asMembers;
1989
1990
1991def unpackFile(sArchive, sDstDir, fnLog, fnError = None, fnFilter = None):
1992 # type: (string, string, (string) -> None, (string) -> None, (string) -> bool) -> list[string]
1993 """
1994 Unpacks the given file if it has a know archive extension, otherwise do
1995 nothing.
1996
1997 fnLog & fnError both take a string parameter.
1998
1999 fnFilter takes a member name (string) and returns True if it's included
2000 and False if excluded.
2001
2002 Returns list of the extracted files (full path) on success.
2003 Returns empty list if not a supported archive format.
2004 Returns None on failure. Raises no exceptions.
2005 """
2006 sBaseNameLower = os.path.basename(sArchive).lower();
2007
2008 #
2009 # Zip file?
2010 #
2011 if sBaseNameLower.endswith('.zip'):
2012 return unpackZipFile(sArchive, sDstDir, fnLog, fnError, fnFilter);
2013
2014 #
2015 # Tarball?
2016 #
2017 if sBaseNameLower.endswith('.tar') \
2018 or sBaseNameLower.endswith('.tar.gz') \
2019 or sBaseNameLower.endswith('.tgz') \
2020 or sBaseNameLower.endswith('.tar.bz2'):
2021 return unpackTarFile(sArchive, sDstDir, fnLog, fnError, fnFilter);
2022
2023 #
2024 # Cannot classify it from the name, so just return that to the caller.
2025 #
2026 fnLog('Not unpacking "%s".' % (sArchive,));
2027 return [];
2028
2029
2030def getDiskUsage(sPath):
2031 """
2032 Get free space of a partition that corresponds to specified sPath in MB.
2033
2034 Returns partition free space value in MB.
2035 """
2036 if platform.system() == 'Windows':
2037 oCTypeFreeSpace = ctypes.c_ulonglong(0);
2038 ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(sPath), None, None,
2039 ctypes.pointer(oCTypeFreeSpace));
2040 cbFreeSpace = oCTypeFreeSpace.value;
2041 else:
2042 oStats = os.statvfs(sPath); # pylint: disable=E1101
2043 cbFreeSpace = long(oStats.f_frsize) * oStats.f_bfree;
2044
2045 # Convert to MB
2046 cMbFreeSpace = long(cbFreeSpace) / (1024 * 1024);
2047
2048 return cMbFreeSpace;
2049
2050
2051#
2052# Unit testing.
2053#
2054
2055# pylint: disable=C0111
2056class BuildCategoryDataTestCase(unittest.TestCase):
2057 def testIntervalSeconds(self):
2058 self.assertEqual(parseIntervalSeconds(formatIntervalSeconds(3600)), (3600, None));
2059 self.assertEqual(parseIntervalSeconds(formatIntervalSeconds(1209438593)), (1209438593, None));
2060 self.assertEqual(parseIntervalSeconds('123'), (123, None));
2061 self.assertEqual(parseIntervalSeconds(123), (123, None));
2062 self.assertEqual(parseIntervalSeconds(99999999999), (99999999999, None));
2063 self.assertEqual(parseIntervalSeconds(''), (0, 'Empty interval string.'));
2064 self.assertEqual(parseIntervalSeconds('1X2'), (3, 'Unknown unit "X".'));
2065 self.assertEqual(parseIntervalSeconds('1 Y3'), (4, 'Unknown unit "Y".'));
2066 self.assertEqual(parseIntervalSeconds('1 Z 4'), (5, 'Unknown unit "Z".'));
2067 self.assertEqual(parseIntervalSeconds('1 hour 2m 5second'), (3725, None));
2068 self.assertEqual(parseIntervalSeconds('1 hour,2m ; 5second'), (3725, None));
2069
2070 def testHasNonAsciiChars(self):
2071 self.assertEqual(hasNonAsciiCharacters(''), False);
2072 self.assertEqual(hasNonAsciiCharacters('asdfgebASDFKJ@#$)(!@#UNASDFKHB*&$%&)@#(!)@(#!(#$&*#$&%*Y@#$IQWN---00;'), False);
2073 self.assertEqual(hasNonAsciiCharacters(u'12039889y!@#$%^&*()0-0asjdkfhoiuyweasdfASDFnvV'), False);
2074 self.assertEqual(hasNonAsciiCharacters(u'\u0079'), False);
2075 self.assertEqual(hasNonAsciiCharacters(u'\u0080'), True);
2076 self.assertEqual(hasNonAsciiCharacters(u'\u0081 \u0100'), True);
2077
2078if __name__ == '__main__':
2079 unittest.main();
2080 # not reached.
2081
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