VirtualBox

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

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

common/utils.py: More python 3 fixes. [d'oh]

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