VirtualBox

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

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

utils.py: printOut/Err fix for pipes and files on 2.x

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

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