VirtualBox

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

Last change on this file since 98988 was 98931, checked in by vboxsync, 2 years ago

ValidationKit: More work to get it running on arm64, bugref:9898 [fix]

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