VirtualBox

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

Last change on this file since 100646 was 99086, checked in by vboxsync, 23 months ago

ValidationKit/{common/utils.py,testdriver/base.py}: Rework the
Solaris-specific core file configuration code to back up any existing
coreadm(8) changes before modifying the system-wide core file
configuration and then restore the original coreadm(8) setup afterwards.

  • 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 99086 2023-03-21 12:43:25Z 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: 99086 $"
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 ]);
1464 #
1465 # Solaris by default creates a core file in the directory of the crashing process with the name 'core'.
1466 #
1467 # As we need to distinguish the core files correlating to their PIDs and have a persistent storage location,
1468 # the host needs to be tweaked via:
1469 #
1470 # ```coreadm -g /var/cores/core.%f.%p```
1471 #
1472 sMatchRegEx = r'core\..*\.%u' % (uPid);
1473 for sDir in asDmpDirs:
1474 sDir = os.path.expandvars(sDir);
1475 if not os.path.isdir(sDir):
1476 continue;
1477 try:
1478 asDirEntries = os.listdir(sDir);
1479 except:
1480 continue;
1481 for sEntry in asDirEntries:
1482 fnLog('Entry: %s' % (os.path.join(sDir, sEntry)));
1483 if re.search(sMatchRegEx, sEntry):
1484 sFull = os.path.join(sDir, sEntry);
1485 fnLog('Found crash dump for %u: %s' % (uPid, sFull,));
1486 fnCrashFile(sFull, True);
1487 else:
1488 pass; ## TODO
1489 return None;
1490
1491
1492#
1493# Time.
1494#
1495
1496#
1497# The following test case shows how time.time() only have ~ms resolution
1498# on Windows (tested W10) and why it therefore makes sense to try use
1499# performance counters.
1500#
1501# Note! We cannot use time.clock() as the timestamp must be portable across
1502# processes. See timeout testcase problem on win hosts (no logs).
1503# Also, time.clock() was axed in python 3.8 (https://bugs.python.org/issue31803).
1504#
1505#import sys;
1506#import time;
1507#from common import utils;
1508#
1509#atSeries = [];
1510#for i in xrange(1,160):
1511# if i == 159: time.sleep(10);
1512# atSeries.append((utils.timestampNano(), long(time.clock() * 1000000000), long(time.time() * 1000000000)));
1513#
1514#tPrev = atSeries[0]
1515#for tCur in atSeries:
1516# print 't1=%+22u, %u' % (tCur[0], tCur[0] - tPrev[0]);
1517# print 't2=%+22u, %u' % (tCur[1], tCur[1] - tPrev[1]);
1518# print 't3=%+22u, %u' % (tCur[2], tCur[2] - tPrev[2]);
1519# print '';
1520# tPrev = tCur
1521#
1522#print 't1=%u' % (atSeries[-1][0] - atSeries[0][0]);
1523#print 't2=%u' % (atSeries[-1][1] - atSeries[0][1]);
1524#print 't3=%u' % (atSeries[-1][2] - atSeries[0][2]);
1525
1526g_fWinUseWinPerfCounter = sys.platform == 'win32';
1527g_fpWinPerfCounterFreq = None;
1528g_oFuncwinQueryPerformanceCounter = None;
1529
1530def _winInitPerfCounter():
1531 """ Initializes the use of performance counters. """
1532 global g_fWinUseWinPerfCounter, g_fpWinPerfCounterFreq, g_oFuncwinQueryPerformanceCounter
1533
1534 uFrequency = ctypes.c_ulonglong(0);
1535 if ctypes.windll.kernel32.QueryPerformanceFrequency(ctypes.byref(uFrequency)):
1536 if uFrequency.value >= 1000:
1537 #print 'uFrequency = %s' % (uFrequency,);
1538 #print 'type(uFrequency) = %s' % (type(uFrequency),);
1539 g_fpWinPerfCounterFreq = float(uFrequency.value);
1540
1541 # Check that querying the counter works too.
1542 global g_oFuncwinQueryPerformanceCounter
1543 g_oFuncwinQueryPerformanceCounter = ctypes.windll.kernel32.QueryPerformanceCounter;
1544 uCurValue = ctypes.c_ulonglong(0);
1545 if g_oFuncwinQueryPerformanceCounter(ctypes.byref(uCurValue)):
1546 if uCurValue.value > 0:
1547 return True;
1548 g_fWinUseWinPerfCounter = False;
1549 return False;
1550
1551def _winFloatTime():
1552 """ Gets floating point time on windows. """
1553 if g_fpWinPerfCounterFreq is not None or _winInitPerfCounter():
1554 uCurValue = ctypes.c_ulonglong(0);
1555 if g_oFuncwinQueryPerformanceCounter(ctypes.byref(uCurValue)):
1556 return float(uCurValue.value) / g_fpWinPerfCounterFreq;
1557 return time.time();
1558
1559def timestampNano():
1560 """
1561 Gets a nanosecond timestamp.
1562 """
1563 if g_fWinUseWinPerfCounter is True:
1564 return long(_winFloatTime() * 1000000000);
1565 return long(time.time() * 1000000000);
1566
1567def timestampMilli():
1568 """
1569 Gets a millisecond timestamp.
1570 """
1571 if g_fWinUseWinPerfCounter is True:
1572 return long(_winFloatTime() * 1000);
1573 return long(time.time() * 1000);
1574
1575def timestampSecond():
1576 """
1577 Gets a second timestamp.
1578 """
1579 if g_fWinUseWinPerfCounter is True:
1580 return long(_winFloatTime());
1581 return long(time.time());
1582
1583def secondsSinceUnixEpoch():
1584 """
1585 Returns unix time, floating point second count since 1970-01-01T00:00:00Z
1586 """
1587 ## ASSUMES This returns unix epoch time on all systems we care about...
1588 return time.time();
1589
1590def getTimePrefix():
1591 """
1592 Returns a timestamp prefix, typically used for logging. UTC.
1593 """
1594 try:
1595 oNow = datetime.datetime.utcnow();
1596 sTs = '%02u:%02u:%02u.%06u' % (oNow.hour, oNow.minute, oNow.second, oNow.microsecond);
1597 except:
1598 sTs = 'getTimePrefix-exception';
1599 return sTs;
1600
1601def getTimePrefixAndIsoTimestamp():
1602 """
1603 Returns current UTC as log prefix and iso timestamp.
1604 """
1605 try:
1606 oNow = datetime.datetime.utcnow();
1607 sTsPrf = '%02u:%02u:%02u.%06u' % (oNow.hour, oNow.minute, oNow.second, oNow.microsecond);
1608 sTsIso = formatIsoTimestamp(oNow);
1609 except:
1610 sTsPrf = sTsIso = 'getTimePrefix-exception';
1611 return (sTsPrf, sTsIso);
1612
1613class UtcTzInfo(datetime.tzinfo):
1614 """UTC TZ Info Class"""
1615 def utcoffset(self, _):
1616 return datetime.timedelta(0);
1617 def tzname(self, _):
1618 return "UTC";
1619 def dst(self, _):
1620 return datetime.timedelta(0);
1621
1622class GenTzInfo(datetime.tzinfo):
1623 """Generic TZ Info Class"""
1624 def __init__(self, offInMin):
1625 datetime.tzinfo.__init__(self);
1626 self.offInMin = offInMin;
1627 def utcoffset(self, _):
1628 return datetime.timedelta(minutes = self.offInMin);
1629 def tzname(self, _):
1630 if self.offInMin >= 0:
1631 return "+%02d%02d" % (self.offInMin // 60, self.offInMin % 60);
1632 return "-%02d%02d" % (-self.offInMin // 60, -self.offInMin % 60);
1633 def dst(self, _):
1634 return datetime.timedelta(0);
1635
1636def formatIsoTimestamp(oNow):
1637 """Formats the datetime object as an ISO timestamp."""
1638 assert oNow.tzinfo is None or isinstance(oNow.tzinfo, UtcTzInfo);
1639 sTs = '%s.%09uZ' % (oNow.strftime('%Y-%m-%dT%H:%M:%S'), oNow.microsecond * 1000);
1640 return sTs;
1641
1642def getIsoTimestamp():
1643 """Returns the current UTC timestamp as a string."""
1644 return formatIsoTimestamp(datetime.datetime.utcnow());
1645
1646def formatShortIsoTimestamp(oNow):
1647 """Formats the datetime object as an ISO timestamp, but w/o microseconds."""
1648 assert oNow.tzinfo is None or isinstance(oNow.tzinfo, UtcTzInfo);
1649 return oNow.strftime('%Y-%m-%dT%H:%M:%SZ');
1650
1651def getShortIsoTimestamp():
1652 """Returns the current UTC timestamp as a string, but w/o microseconds."""
1653 return formatShortIsoTimestamp(datetime.datetime.utcnow());
1654
1655def convertDateTimeToZulu(oDateTime):
1656 """ Converts oDateTime to zulu time if it has timezone info. """
1657 if oDateTime.tzinfo is not None:
1658 oDateTime = oDateTime.astimezone(UtcTzInfo());
1659 else:
1660 oDateTime = oDateTime.replace(tzinfo = UtcTzInfo());
1661 return oDateTime;
1662
1663def parseIsoTimestamp(sTs):
1664 """
1665 Parses a typical ISO timestamp, returing a datetime object, reasonably
1666 forgiving, but will throw weird indexing/conversion errors if the input
1667 is malformed.
1668 """
1669 # YYYY-MM-DD
1670 iYear = int(sTs[0:4]);
1671 assert(sTs[4] == '-');
1672 iMonth = int(sTs[5:7]);
1673 assert(sTs[7] == '-');
1674 iDay = int(sTs[8:10]);
1675
1676 # Skip separator
1677 sTime = sTs[10:];
1678 while sTime[0] in 'Tt \t\n\r':
1679 sTime = sTime[1:];
1680
1681 # HH:MM[:SS]
1682 iHour = int(sTime[0:2]);
1683 assert(sTime[2] == ':');
1684 iMin = int(sTime[3:5]);
1685 if sTime[5] == ':':
1686 iSec = int(sTime[6:8]);
1687
1688 # Fraction?
1689 offTime = 8;
1690 iMicroseconds = 0;
1691 if offTime < len(sTime) and sTime[offTime] in '.,':
1692 offTime += 1;
1693 cchFraction = 0;
1694 while offTime + cchFraction < len(sTime) and sTime[offTime + cchFraction] in '0123456789':
1695 cchFraction += 1;
1696 if cchFraction > 0:
1697 iMicroseconds = int(sTime[offTime : (offTime + cchFraction)]);
1698 offTime += cchFraction;
1699 while cchFraction < 6:
1700 iMicroseconds *= 10;
1701 cchFraction += 1;
1702 while cchFraction > 6:
1703 iMicroseconds = iMicroseconds // 10;
1704 cchFraction -= 1;
1705
1706 else:
1707 iSec = 0;
1708 iMicroseconds = 0;
1709 offTime = 5;
1710
1711 # Naive?
1712 if offTime >= len(sTime):
1713 return datetime.datetime(iYear, iMonth, iDay, iHour, iMin, iSec, iMicroseconds);
1714
1715 # Zulu?
1716 if offTime >= len(sTime) or sTime[offTime] in 'Zz':
1717 return datetime.datetime(iYear, iMonth, iDay, iHour, iMin, iSec, iMicroseconds, tzinfo = UtcTzInfo());
1718
1719 # Some kind of offset afterwards, and strptime is useless. sigh.
1720 if sTime[offTime] in '+-':
1721 chSign = sTime[offTime];
1722 offTime += 1;
1723 cMinTz = int(sTime[offTime : (offTime + 2)]) * 60;
1724 offTime += 2;
1725 if offTime < len(sTime) and sTime[offTime] in ':':
1726 offTime += 1;
1727 if offTime + 2 <= len(sTime):
1728 cMinTz += int(sTime[offTime : (offTime + 2)]);
1729 offTime += 2;
1730 assert offTime == len(sTime);
1731 if chSign == '-':
1732 cMinTz = -cMinTz;
1733 return datetime.datetime(iYear, iMonth, iDay, iHour, iMin, iSec, iMicroseconds, tzinfo = GenTzInfo(cMinTz));
1734 assert False, sTs;
1735 return datetime.datetime(iYear, iMonth, iDay, iHour, iMin, iSec, iMicroseconds);
1736
1737def normalizeIsoTimestampToZulu(sTs):
1738 """
1739 Takes a iso timestamp string and normalizes it (basically parseIsoTimestamp
1740 + convertDateTimeToZulu + formatIsoTimestamp).
1741 Returns ISO tiemstamp string.
1742 """
1743 return formatIsoTimestamp(convertDateTimeToZulu(parseIsoTimestamp(sTs)));
1744
1745def getLocalHourOfWeek():
1746 """ Local hour of week (0 based). """
1747 oNow = datetime.datetime.now();
1748 return (oNow.isoweekday() - 1) * 24 + oNow.hour;
1749
1750
1751def formatIntervalSeconds(cSeconds):
1752 """ Format a seconds interval into a nice 01h 00m 22s string """
1753 # Two simple special cases.
1754 if cSeconds < 60:
1755 return '%ss' % (cSeconds,);
1756 if cSeconds < 3600:
1757 cMins = cSeconds // 60;
1758 cSecs = cSeconds % 60;
1759 if cSecs == 0:
1760 return '%sm' % (cMins,);
1761 return '%sm %ss' % (cMins, cSecs,);
1762
1763 # Generic and a bit slower.
1764 cDays = cSeconds // 86400;
1765 cSeconds %= 86400;
1766 cHours = cSeconds // 3600;
1767 cSeconds %= 3600;
1768 cMins = cSeconds // 60;
1769 cSecs = cSeconds % 60;
1770 sRet = '';
1771 if cDays > 0:
1772 sRet = '%sd ' % (cDays,);
1773 if cHours > 0:
1774 sRet += '%sh ' % (cHours,);
1775 if cMins > 0:
1776 sRet += '%sm ' % (cMins,);
1777 if cSecs > 0:
1778 sRet += '%ss ' % (cSecs,);
1779 assert sRet; assert sRet[-1] == ' ';
1780 return sRet[:-1];
1781
1782def formatIntervalSeconds2(oSeconds):
1783 """
1784 Flexible input version of formatIntervalSeconds for use in WUI forms where
1785 data is usually already string form.
1786 """
1787 if isinstance(oSeconds, (int, long)):
1788 return formatIntervalSeconds(oSeconds);
1789 if not isString(oSeconds):
1790 try:
1791 lSeconds = long(oSeconds);
1792 except:
1793 pass;
1794 else:
1795 if lSeconds >= 0:
1796 return formatIntervalSeconds2(lSeconds);
1797 return oSeconds;
1798
1799def parseIntervalSeconds(sString):
1800 """
1801 Reverse of formatIntervalSeconds.
1802
1803 Returns (cSeconds, sError), where sError is None on success.
1804 """
1805
1806 # We might given non-strings, just return them without any fuss.
1807 if not isString(sString):
1808 if isinstance(sString, (int, long)) or sString is None:
1809 return (sString, None);
1810 ## @todo time/date objects?
1811 return (int(sString), None);
1812
1813 # Strip it and make sure it's not empty.
1814 sString = sString.strip();
1815 if not sString:
1816 return (0, 'Empty interval string.');
1817
1818 #
1819 # Split up the input into a list of 'valueN, unitN, ...'.
1820 #
1821 # Don't want to spend too much time trying to make re.split do exactly what
1822 # I need here, so please forgive the extra pass I'm making here.
1823 #
1824 asRawParts = re.split(r'\s*([0-9]+)\s*([^0-9,;]*)[\s,;]*', sString);
1825 asParts = [];
1826 for sPart in asRawParts:
1827 sPart = sPart.strip();
1828 if sPart:
1829 asParts.append(sPart);
1830 if not asParts:
1831 return (0, 'Empty interval string or something?');
1832
1833 #
1834 # Process them one or two at the time.
1835 #
1836 cSeconds = 0;
1837 asErrors = [];
1838 i = 0;
1839 while i < len(asParts):
1840 sNumber = asParts[i];
1841 i += 1;
1842 if sNumber.isdigit():
1843 iNumber = int(sNumber);
1844
1845 sUnit = 's';
1846 if i < len(asParts) and not asParts[i].isdigit():
1847 sUnit = asParts[i];
1848 i += 1;
1849
1850 sUnitLower = sUnit.lower();
1851 if sUnitLower in [ 's', 'se', 'sec', 'second', 'seconds' ]:
1852 pass;
1853 elif sUnitLower in [ 'm', 'mi', 'min', 'minute', 'minutes' ]:
1854 iNumber *= 60;
1855 elif sUnitLower in [ 'h', 'ho', 'hou', 'hour', 'hours' ]:
1856 iNumber *= 3600;
1857 elif sUnitLower in [ 'd', 'da', 'day', 'days' ]:
1858 iNumber *= 86400;
1859 elif sUnitLower in [ 'w', 'week', 'weeks' ]:
1860 iNumber *= 7 * 86400;
1861 else:
1862 asErrors.append('Unknown unit "%s".' % (sUnit,));
1863 cSeconds += iNumber;
1864 else:
1865 asErrors.append('Bad number "%s".' % (sNumber,));
1866 return (cSeconds, None if not asErrors else ' '.join(asErrors));
1867
1868def formatIntervalHours(cHours):
1869 """ Format a hours interval into a nice 1w 2d 1h string. """
1870 # Simple special cases.
1871 if cHours < 24:
1872 return '%sh' % (cHours,);
1873
1874 # Generic and a bit slower.
1875 cWeeks = cHours / (7 * 24);
1876 cHours %= 7 * 24;
1877 cDays = cHours / 24;
1878 cHours %= 24;
1879 sRet = '';
1880 if cWeeks > 0:
1881 sRet = '%sw ' % (cWeeks,);
1882 if cDays > 0:
1883 sRet = '%sd ' % (cDays,);
1884 if cHours > 0:
1885 sRet += '%sh ' % (cHours,);
1886 assert sRet; assert sRet[-1] == ' ';
1887 return sRet[:-1];
1888
1889def parseIntervalHours(sString):
1890 """
1891 Reverse of formatIntervalHours.
1892
1893 Returns (cHours, sError), where sError is None on success.
1894 """
1895
1896 # We might given non-strings, just return them without any fuss.
1897 if not isString(sString):
1898 if isinstance(sString, (int, long)) or sString is None:
1899 return (sString, None);
1900 ## @todo time/date objects?
1901 return (int(sString), None);
1902
1903 # Strip it and make sure it's not empty.
1904 sString = sString.strip();
1905 if not sString:
1906 return (0, 'Empty interval string.');
1907
1908 #
1909 # Split up the input into a list of 'valueN, unitN, ...'.
1910 #
1911 # Don't want to spend too much time trying to make re.split do exactly what
1912 # I need here, so please forgive the extra pass I'm making here.
1913 #
1914 asRawParts = re.split(r'\s*([0-9]+)\s*([^0-9,;]*)[\s,;]*', sString);
1915 asParts = [];
1916 for sPart in asRawParts:
1917 sPart = sPart.strip();
1918 if sPart:
1919 asParts.append(sPart);
1920 if not asParts:
1921 return (0, 'Empty interval string or something?');
1922
1923 #
1924 # Process them one or two at the time.
1925 #
1926 cHours = 0;
1927 asErrors = [];
1928 i = 0;
1929 while i < len(asParts):
1930 sNumber = asParts[i];
1931 i += 1;
1932 if sNumber.isdigit():
1933 iNumber = int(sNumber);
1934
1935 sUnit = 'h';
1936 if i < len(asParts) and not asParts[i].isdigit():
1937 sUnit = asParts[i];
1938 i += 1;
1939
1940 sUnitLower = sUnit.lower();
1941 if sUnitLower in [ 'h', 'ho', 'hou', 'hour', 'hours' ]:
1942 pass;
1943 elif sUnitLower in [ 'd', 'da', 'day', 'days' ]:
1944 iNumber *= 24;
1945 elif sUnitLower in [ 'w', 'week', 'weeks' ]:
1946 iNumber *= 7 * 24;
1947 else:
1948 asErrors.append('Unknown unit "%s".' % (sUnit,));
1949 cHours += iNumber;
1950 else:
1951 asErrors.append('Bad number "%s".' % (sNumber,));
1952 return (cHours, None if not asErrors else ' '.join(asErrors));
1953
1954
1955#
1956# Introspection.
1957#
1958
1959def getCallerName(oFrame=None, iFrame=2):
1960 """
1961 Returns the name of the caller's caller.
1962 """
1963 if oFrame is None:
1964 try:
1965 raise Exception();
1966 except:
1967 oFrame = sys.exc_info()[2].tb_frame.f_back;
1968 while iFrame > 1:
1969 if oFrame is not None:
1970 oFrame = oFrame.f_back;
1971 iFrame = iFrame - 1;
1972 if oFrame is not None:
1973 sName = '%s:%u' % (oFrame.f_code.co_name, oFrame.f_lineno);
1974 return sName;
1975 return "unknown";
1976
1977
1978def getXcptInfo(cFrames = 1):
1979 """
1980 Gets text detailing the exception. (Good for logging.)
1981 Returns list of info strings.
1982 """
1983
1984 #
1985 # Try get exception info.
1986 #
1987 try:
1988 oType, oValue, oTraceback = sys.exc_info();
1989 except:
1990 oType = oValue = oTraceback = None;
1991 if oType is not None:
1992
1993 #
1994 # Try format the info
1995 #
1996 asRet = [];
1997 try:
1998 try:
1999 asRet = asRet + traceback.format_exception_only(oType, oValue);
2000 asTraceBack = traceback.format_tb(oTraceback);
2001 if cFrames is not None and cFrames <= 1:
2002 asRet.append(asTraceBack[-1]);
2003 else:
2004 asRet.append('Traceback:')
2005 for iFrame in range(min(cFrames, len(asTraceBack))):
2006 asRet.append(asTraceBack[-iFrame - 1]);
2007 asRet.append('Stack:')
2008 asRet = asRet + traceback.format_stack(oTraceback.tb_frame.f_back, cFrames);
2009 except:
2010 asRet.append('internal-error: Hit exception #2! %s' % (traceback.format_exc(),));
2011
2012 if not asRet:
2013 asRet.append('No exception info...');
2014 except:
2015 asRet.append('internal-error: Hit exception! %s' % (traceback.format_exc(),));
2016 else:
2017 asRet = ['Couldn\'t find exception traceback.'];
2018 return asRet;
2019
2020
2021def getObjectTypeName(oObject):
2022 """
2023 Get the type name of the given object.
2024 """
2025 if oObject is None:
2026 return 'None';
2027
2028 # Get the type object.
2029 try:
2030 oType = type(oObject);
2031 except:
2032 return 'type-throws-exception';
2033
2034 # Python 2.x only: Handle old-style object wrappers.
2035 if sys.version_info[0] < 3:
2036 try:
2037 from types import InstanceType; # pylint: disable=no-name-in-module
2038 if oType == InstanceType:
2039 oType = oObject.__class__;
2040 except:
2041 pass;
2042
2043 # Get the name.
2044 try:
2045 return oType.__name__;
2046 except:
2047 return '__type__-throws-exception';
2048
2049
2050def chmodPlusX(sFile):
2051 """
2052 Makes the specified file or directory executable.
2053 Returns success indicator, no exceptions.
2054
2055 Note! Symbolic links are followed and the target will be changed.
2056 """
2057 try:
2058 oStat = os.stat(sFile);
2059 except:
2060 return False;
2061 try:
2062 os.chmod(sFile, oStat.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH);
2063 except:
2064 return False;
2065 return True;
2066
2067
2068#
2069# TestSuite stuff.
2070#
2071
2072def isRunningFromCheckout(cScriptDepth = 1):
2073 """
2074 Checks if we're running from the SVN checkout or not.
2075 """
2076
2077 try:
2078 sFile = __file__;
2079 cScriptDepth = 1;
2080 except:
2081 sFile = sys.argv[0];
2082
2083 sDir = os.path.abspath(sFile);
2084 while cScriptDepth >= 0:
2085 sDir = os.path.dirname(sDir);
2086 if os.path.exists(os.path.join(sDir, 'Makefile.kmk')) \
2087 or os.path.exists(os.path.join(sDir, 'Makefile.kup')):
2088 return True;
2089 cScriptDepth -= 1;
2090
2091 return False;
2092
2093
2094#
2095# Bourne shell argument fun.
2096#
2097
2098
2099def argsSplit(sCmdLine):
2100 """
2101 Given a bourne shell command line invocation, split it up into arguments
2102 assuming IFS is space.
2103 Returns None on syntax error.
2104 """
2105 ## @todo bourne shell argument parsing!
2106 return sCmdLine.split(' ');
2107
2108def argsGetFirst(sCmdLine):
2109 """
2110 Given a bourne shell command line invocation, get return the first argument
2111 assuming IFS is space.
2112 Returns None on invalid syntax, otherwise the parsed and unescaped argv[0] string.
2113 """
2114 asArgs = argsSplit(sCmdLine);
2115 if not asArgs:
2116 return None;
2117
2118 return asArgs[0];
2119
2120#
2121# String helpers.
2122#
2123
2124def stricmp(sFirst, sSecond):
2125 """
2126 Compares to strings in an case insensitive fashion.
2127
2128 Python doesn't seem to have any way of doing the correctly, so this is just
2129 an approximation using lower.
2130 """
2131 if sFirst == sSecond:
2132 return 0;
2133 sLower1 = sFirst.lower();
2134 sLower2 = sSecond.lower();
2135 if sLower1 == sLower2:
2136 return 0;
2137 if sLower1 < sLower2:
2138 return -1;
2139 return 1;
2140
2141
2142def versionCompare(sVer1, sVer2):
2143 """
2144 Compares to version strings in a fashion similar to RTStrVersionCompare.
2145 """
2146
2147 ## @todo implement me!!
2148
2149 if sVer1 == sVer2:
2150 return 0;
2151 if sVer1 < sVer2:
2152 return -1;
2153 return 1;
2154
2155
2156def formatNumber(lNum, sThousandSep = ' '):
2157 """
2158 Formats a decimal number with pretty separators.
2159 """
2160 sNum = str(lNum);
2161 sRet = sNum[-3:];
2162 off = len(sNum) - 3;
2163 while off > 0:
2164 off -= 3;
2165 sRet = sNum[(off if off >= 0 else 0):(off + 3)] + sThousandSep + sRet;
2166 return sRet;
2167
2168
2169def formatNumberNbsp(lNum):
2170 """
2171 Formats a decimal number with pretty separators.
2172 """
2173 sRet = formatNumber(lNum);
2174 return unicode(sRet).replace(' ', u'\u00a0');
2175
2176
2177def isString(oString):
2178 """
2179 Checks if the object is a string object, hiding difference between python 2 and 3.
2180
2181 Returns True if it's a string of some kind.
2182 Returns False if not.
2183 """
2184 if sys.version_info[0] >= 3:
2185 return isinstance(oString, str);
2186 return isinstance(oString, basestring); # pylint: disable=undefined-variable
2187
2188
2189def hasNonAsciiCharacters(sText):
2190 """
2191 Returns True is specified string has non-ASCII characters, False if ASCII only.
2192 """
2193 if isString(sText):
2194 for ch in sText:
2195 if ord(ch) >= 128:
2196 return True;
2197 else:
2198 # Probably byte array or some such thing.
2199 for ch in sText:
2200 if ch >= 128 or ch < 0:
2201 return True;
2202 return False;
2203
2204
2205#
2206# Unpacking.
2207#
2208
2209def unpackZipFile(sArchive, sDstDir, fnLog, fnError = None, fnFilter = None):
2210 # type: (string, string, (string) -> None, (string) -> None, (string) -> bool) -> list[string]
2211 """
2212 Worker for unpackFile that deals with ZIP files, same function signature.
2213 """
2214 import zipfile
2215 if fnError is None:
2216 fnError = fnLog;
2217
2218 fnLog('Unzipping "%s" to "%s"...' % (sArchive, sDstDir));
2219
2220 # Open it.
2221 try: oZipFile = zipfile.ZipFile(sArchive, 'r'); # pylint: disable=consider-using-with
2222 except Exception as oXcpt:
2223 fnError('Error opening "%s" for unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt,));
2224 return None;
2225
2226 # Extract all members.
2227 asMembers = [];
2228 try:
2229 for sMember in oZipFile.namelist():
2230 if fnFilter is None or fnFilter(sMember) is not False:
2231 if sMember.endswith('/'):
2232 os.makedirs(os.path.join(sDstDir, sMember.replace('/', os.path.sep)), 0x1fd); # octal: 0775 (python 3/2)
2233 else:
2234 oZipFile.extract(sMember, sDstDir);
2235 asMembers.append(os.path.join(sDstDir, sMember.replace('/', os.path.sep)));
2236 except Exception as oXcpt:
2237 fnError('Error unpacking "%s" into "%s": %s' % (sArchive, sDstDir, oXcpt));
2238 asMembers = None;
2239
2240 # close it.
2241 try: oZipFile.close();
2242 except Exception as oXcpt:
2243 fnError('Error closing "%s" after unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt));
2244 asMembers = None;
2245
2246 return asMembers;
2247
2248
2249## Set if we've replaced tarfile.copyfileobj with __mytarfilecopyfileobj already.
2250g_fTarCopyFileObjOverriddend = False;
2251
2252def __mytarfilecopyfileobj(src, dst, length = None, exception = OSError, bufsize = None):
2253 """ tarfile.copyfileobj with different buffer size (16384 is slow on windows). """
2254 _ = bufsize;
2255 if length is None:
2256 __myshutilcopyfileobj(src, dst, g_cbGoodBufferSize);
2257 elif length > 0:
2258 cFull, cbRemainder = divmod(length, g_cbGoodBufferSize);
2259 for _ in xrange(cFull):
2260 abBuffer = src.read(g_cbGoodBufferSize);
2261 dst.write(abBuffer);
2262 if len(abBuffer) != g_cbGoodBufferSize:
2263 raise exception('unexpected end of source file');
2264 if cbRemainder > 0:
2265 abBuffer = src.read(cbRemainder);
2266 dst.write(abBuffer);
2267 if len(abBuffer) != cbRemainder:
2268 raise exception('unexpected end of source file');
2269
2270
2271def unpackTarFile(sArchive, sDstDir, fnLog, fnError = None, fnFilter = None):
2272 # type: (string, string, (string) -> None, (string) -> None, (string) -> bool) -> list[string]
2273 """
2274 Worker for unpackFile that deals with tarballs, same function signature.
2275 """
2276 import shutil;
2277 import tarfile;
2278 if fnError is None:
2279 fnError = fnLog;
2280
2281 fnLog('Untarring "%s" to "%s"...' % (sArchive, sDstDir));
2282
2283 #
2284 # Default buffer sizes of 16384 bytes is causing too many syscalls on Windows.
2285 # 60%+ speedup for python 2.7 and 50%+ speedup for python 3.5, both on windows with PDBs.
2286 # 20%+ speedup for python 2.7 and 15%+ speedup for python 3.5, both on windows skipping PDBs.
2287 #
2288 if True is True: # pylint: disable=comparison-with-itself,comparison-of-constants
2289 __installShUtilHacks(shutil);
2290 global g_fTarCopyFileObjOverriddend;
2291 if g_fTarCopyFileObjOverriddend is False:
2292 g_fTarCopyFileObjOverriddend = True;
2293 #if sys.hexversion < 0x03060000:
2294 tarfile.copyfileobj = __mytarfilecopyfileobj;
2295
2296 #
2297 # Open it.
2298 #
2299 # Note! We not using 'r:*' because we cannot allow seeking compressed files!
2300 # That's how we got a 13 min unpack time for VBoxAll on windows (hardlinked pdb).
2301 #
2302 try:
2303 if sys.hexversion >= 0x03060000:
2304 oTarFile = tarfile.open(sArchive, 'r|*', # pylint: disable=consider-using-with
2305 bufsize = g_cbGoodBufferSize, copybufsize = g_cbGoodBufferSize);
2306 else:
2307 oTarFile = tarfile.open(sArchive, 'r|*', bufsize = g_cbGoodBufferSize); # pylint: disable=consider-using-with
2308 except Exception as oXcpt:
2309 fnError('Error opening "%s" for unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt,));
2310 return None;
2311
2312 # Extract all members.
2313 asMembers = [];
2314 try:
2315 for oTarInfo in oTarFile:
2316 try:
2317 if fnFilter is None or fnFilter(oTarInfo.name) is not False:
2318 if oTarInfo.islnk():
2319 # Links are trouble, especially on Windows. We must avoid the falling that will end up seeking
2320 # in the compressed tar stream. So, fall back on shutil.copy2 instead.
2321 sLinkFile = os.path.join(sDstDir, oTarInfo.name.rstrip('/').replace('/', os.path.sep));
2322 sLinkTarget = os.path.join(sDstDir, oTarInfo.linkname.rstrip('/').replace('/', os.path.sep));
2323 sParentDir = os.path.dirname(sLinkFile);
2324 try: os.unlink(sLinkFile);
2325 except: pass;
2326 if sParentDir and not os.path.exists(sParentDir):
2327 os.makedirs(sParentDir);
2328 try: os.link(sLinkTarget, sLinkFile);
2329 except: shutil.copy2(sLinkTarget, sLinkFile);
2330 else:
2331 if oTarInfo.isdir():
2332 # Just make sure the user (we) got full access to dirs. Don't bother getting it 100% right.
2333 oTarInfo.mode |= 0x1c0; # (octal: 0700)
2334 oTarFile.extract(oTarInfo, sDstDir);
2335 asMembers.append(os.path.join(sDstDir, oTarInfo.name.replace('/', os.path.sep)));
2336 except Exception as oXcpt:
2337 fnError('Error unpacking "%s" member "%s" into "%s": %s' % (sArchive, oTarInfo.name, sDstDir, oXcpt));
2338 for sAttr in [ 'name', 'linkname', 'type', 'mode', 'size', 'mtime', 'uid', 'uname', 'gid', 'gname' ]:
2339 fnError('Info: %8s=%s' % (sAttr, getattr(oTarInfo, sAttr),));
2340 for sFn in [ 'isdir', 'isfile', 'islnk', 'issym' ]:
2341 fnError('Info: %8s=%s' % (sFn, getattr(oTarInfo, sFn)(),));
2342 asMembers = None;
2343 break;
2344 except Exception as oXcpt:
2345 fnError('Error unpacking "%s" into "%s": %s' % (sArchive, sDstDir, oXcpt));
2346 asMembers = None;
2347
2348 #
2349 # Finally, close it.
2350 #
2351 try: oTarFile.close();
2352 except Exception as oXcpt:
2353 fnError('Error closing "%s" after unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt));
2354 asMembers = None;
2355
2356 return asMembers;
2357
2358
2359def unpackFile(sArchive, sDstDir, fnLog, fnError = None, fnFilter = None):
2360 # type: (string, string, (string) -> None, (string) -> None, (string) -> bool) -> list[string]
2361 """
2362 Unpacks the given file if it has a know archive extension, otherwise do
2363 nothing.
2364
2365 fnLog & fnError both take a string parameter.
2366
2367 fnFilter takes a member name (string) and returns True if it's included
2368 and False if excluded.
2369
2370 Returns list of the extracted files (full path) on success.
2371 Returns empty list if not a supported archive format.
2372 Returns None on failure. Raises no exceptions.
2373 """
2374 sBaseNameLower = os.path.basename(sArchive).lower();
2375
2376 #
2377 # Zip file?
2378 #
2379 if sBaseNameLower.endswith('.zip'):
2380 return unpackZipFile(sArchive, sDstDir, fnLog, fnError, fnFilter);
2381
2382 #
2383 # Tarball?
2384 #
2385 if sBaseNameLower.endswith('.tar') \
2386 or sBaseNameLower.endswith('.tar.gz') \
2387 or sBaseNameLower.endswith('.tgz') \
2388 or sBaseNameLower.endswith('.tar.bz2'):
2389 return unpackTarFile(sArchive, sDstDir, fnLog, fnError, fnFilter);
2390
2391 #
2392 # Cannot classify it from the name, so just return that to the caller.
2393 #
2394 fnLog('Not unpacking "%s".' % (sArchive,));
2395 return [];
2396
2397
2398#
2399# Misc.
2400#
2401def areBytesEqual(oLeft, oRight):
2402 """
2403 Compares two byte arrays, strings or whatnot.
2404
2405 returns true / false accordingly.
2406 """
2407
2408 # If both are None, consider them equal (bogus?):
2409 if oLeft is None and oRight is None:
2410 return True;
2411
2412 # If just one is None, they can't match:
2413 if oLeft is None or oRight is None:
2414 return False;
2415
2416 # If both have the same type, use the compare operator of the class:
2417 if type(oLeft) is type(oRight):
2418 #print('same type: %s' % (oLeft == oRight,));
2419 return oLeft == oRight;
2420
2421 # On the offchance that they're both strings, but of different types.
2422 if isString(oLeft) and isString(oRight):
2423 #print('string compare: %s' % (oLeft == oRight,));
2424 return oLeft == oRight;
2425
2426 #
2427 # See if byte/buffer stuff that can be compared directory. If not convert
2428 # strings to bytes.
2429 #
2430 # Note! For 2.x, we must convert both sides to the buffer type or the
2431 # comparison may fail despite it working okay in test cases.
2432 #
2433 if sys.version_info[0] >= 3:
2434 if isinstance(oLeft, (bytearray, memoryview, bytes)) and isinstance(oRight, (bytearray, memoryview, bytes)): # pylint: disable=undefined-variable
2435 return oLeft == oRight;
2436
2437 if isString(oLeft):
2438 try: oLeft = bytes(oLeft, 'utf-8');
2439 except: pass;
2440 if isString(oRight):
2441 try: oRight = bytes(oRight, 'utf-8');
2442 except: pass;
2443 else:
2444 if isinstance(oLeft, (bytearray, buffer)) and isinstance(oRight, (bytearray, buffer)): # pylint: disable=undefined-variable
2445 if isinstance(oLeft, bytearray):
2446 oLeft = buffer(oLeft); # pylint: disable=redefined-variable-type,undefined-variable
2447 else:
2448 oRight = buffer(oRight); # pylint: disable=redefined-variable-type,undefined-variable
2449 #print('buf/byte #1 compare: %s (%s vs %s)' % (oLeft == oRight, type(oLeft), type(oRight),));
2450 return oLeft == oRight;
2451
2452 if isString(oLeft):
2453 try: oLeft = bytearray(oLeft, 'utf-8'); # pylint: disable=redefined-variable-type
2454 except: pass;
2455 if isString(oRight):
2456 try: oRight = bytearray(oRight, 'utf-8'); # pylint: disable=redefined-variable-type
2457 except: pass;
2458
2459 # Check if we now have the same type for both:
2460 if type(oLeft) is type(oRight):
2461 #print('same type now: %s' % (oLeft == oRight,));
2462 return oLeft == oRight;
2463
2464 # Check if we now have buffer/memoryview vs bytes/bytesarray again.
2465 if sys.version_info[0] >= 3:
2466 if isinstance(oLeft, (bytearray, memoryview, bytes)) and isinstance(oRight, (bytearray, memoryview, bytes)): # pylint: disable=undefined-variable
2467 return oLeft == oRight;
2468 else:
2469 if isinstance(oLeft, (bytearray, buffer)) and isinstance(oRight, (bytearray, buffer)): # pylint: disable=undefined-variable
2470 if isinstance(oLeft, bytearray):
2471 oLeft = buffer(oLeft); # pylint: disable=redefined-variable-type,undefined-variable
2472 else:
2473 oRight = buffer(oRight); # pylint: disable=redefined-variable-type,undefined-variable
2474 #print('buf/byte #2 compare: %s (%s vs %s)' % (oLeft == oRight, type(oLeft), type(oRight),));
2475 return oLeft == oRight;
2476
2477 # Do item by item comparison:
2478 if len(oLeft) != len(oRight):
2479 #print('different length: %s vs %s' % (len(oLeft), len(oRight)));
2480 return False;
2481 i = len(oLeft);
2482 while i > 0:
2483 i = i - 1;
2484
2485 iElmLeft = oLeft[i];
2486 if not isinstance(iElmLeft, int) and not isinstance(iElmLeft, long):
2487 iElmLeft = ord(iElmLeft);
2488
2489 iElmRight = oRight[i];
2490 if not isinstance(iElmRight, int) and not isinstance(iElmRight, long):
2491 iElmRight = ord(iElmRight);
2492
2493 if iElmLeft != iElmRight:
2494 #print('element %d differs: %x %x' % (i, iElmLeft, iElmRight,));
2495 return False;
2496 return True;
2497
2498
2499def calcCrc32OfFile(sFile):
2500 """
2501 Simple helper for calculating the CRC32 of a file.
2502
2503 Throws stuff if the file cannot be opened or read successfully.
2504 """
2505 import zlib;
2506
2507 uCrc32 = 0;
2508 with open(sFile, 'rb') as oFile: # pylint: disable=unspecified-encoding
2509 while True:
2510 oBuf = oFile.read(1024 * 1024);
2511 if not oBuf:
2512 break
2513 uCrc32 = zlib.crc32(oBuf, uCrc32);
2514
2515 return uCrc32 % 2**32;
2516
2517
2518#
2519# Unit testing.
2520#
2521
2522# pylint: disable=missing-docstring
2523# pylint: disable=undefined-variable
2524class BuildCategoryDataTestCase(unittest.TestCase):
2525 def testIntervalSeconds(self):
2526 self.assertEqual(parseIntervalSeconds(formatIntervalSeconds(3600)), (3600, None));
2527 self.assertEqual(parseIntervalSeconds(formatIntervalSeconds(1209438593)), (1209438593, None));
2528 self.assertEqual(parseIntervalSeconds('123'), (123, None));
2529 self.assertEqual(parseIntervalSeconds(123), (123, None));
2530 self.assertEqual(parseIntervalSeconds(99999999999), (99999999999, None));
2531 self.assertEqual(parseIntervalSeconds(''), (0, 'Empty interval string.'));
2532 self.assertEqual(parseIntervalSeconds('1X2'), (3, 'Unknown unit "X".'));
2533 self.assertEqual(parseIntervalSeconds('1 Y3'), (4, 'Unknown unit "Y".'));
2534 self.assertEqual(parseIntervalSeconds('1 Z 4'), (5, 'Unknown unit "Z".'));
2535 self.assertEqual(parseIntervalSeconds('1 hour 2m 5second'), (3725, None));
2536 self.assertEqual(parseIntervalSeconds('1 hour,2m ; 5second'), (3725, None));
2537
2538 def testZuluNormalization(self):
2539 self.assertEqual(normalizeIsoTimestampToZulu('2011-01-02T03:34:25.000000000Z'), '2011-01-02T03:34:25.000000000Z');
2540 self.assertEqual(normalizeIsoTimestampToZulu('2011-01-02T03:04:25-0030'), '2011-01-02T03:34:25.000000000Z');
2541 self.assertEqual(normalizeIsoTimestampToZulu('2011-01-02T03:04:25+0030'), '2011-01-02T02:34:25.000000000Z');
2542 self.assertEqual(normalizeIsoTimestampToZulu('2020-03-20T20:47:39,832312863+01:00'), '2020-03-20T19:47:39.832312000Z');
2543 self.assertEqual(normalizeIsoTimestampToZulu('2020-03-20T20:47:39,832312863-02:00'), '2020-03-20T22:47:39.832312000Z');
2544
2545 def testHasNonAsciiChars(self):
2546 self.assertEqual(hasNonAsciiCharacters(''), False);
2547 self.assertEqual(hasNonAsciiCharacters('asdfgebASDFKJ@#$)(!@#UNASDFKHB*&$%&)@#(!)@(#!(#$&*#$&%*Y@#$IQWN---00;'), False);
2548 self.assertEqual(hasNonAsciiCharacters('\x80 '), True);
2549 self.assertEqual(hasNonAsciiCharacters('\x79 '), False);
2550 self.assertEqual(hasNonAsciiCharacters(u'12039889y!@#$%^&*()0-0asjdkfhoiuyweasdfASDFnvV'), False);
2551 self.assertEqual(hasNonAsciiCharacters(u'\u0079'), False);
2552 self.assertEqual(hasNonAsciiCharacters(u'\u0080'), True);
2553 self.assertEqual(hasNonAsciiCharacters(u'\u0081 \u0100'), True);
2554 self.assertEqual(hasNonAsciiCharacters(b'\x20\x20\x20'), False);
2555 self.assertEqual(hasNonAsciiCharacters(b'\x20\x81\x20'), True);
2556
2557 def testAreBytesEqual(self):
2558 self.assertEqual(areBytesEqual(None, None), True);
2559 self.assertEqual(areBytesEqual(None, ''), False);
2560 self.assertEqual(areBytesEqual('', ''), True);
2561 self.assertEqual(areBytesEqual('1', '1'), True);
2562 self.assertEqual(areBytesEqual('12345', '1234'), False);
2563 self.assertEqual(areBytesEqual('1234', '1234'), True);
2564 self.assertEqual(areBytesEqual('1234', b'1234'), True);
2565 self.assertEqual(areBytesEqual(b'1234', b'1234'), True);
2566 self.assertEqual(areBytesEqual(b'1234', '1234'), True);
2567 self.assertEqual(areBytesEqual(b'1234', bytearray([0x31,0x32,0x33,0x34])), True);
2568 self.assertEqual(areBytesEqual('1234', bytearray([0x31,0x32,0x33,0x34])), True);
2569 self.assertEqual(areBytesEqual(u'1234', bytearray([0x31,0x32,0x33,0x34])), True);
2570 self.assertEqual(areBytesEqual(bytearray([0x31,0x32,0x33,0x34]), bytearray([0x31,0x32,0x33,0x34])), True);
2571 self.assertEqual(areBytesEqual(bytearray([0x31,0x32,0x33,0x34]), '1224'), False);
2572 self.assertEqual(areBytesEqual(bytearray([0x31,0x32,0x33,0x34]), bytearray([0x31,0x32,0x32,0x34])), False);
2573 if sys.version_info[0] >= 3:
2574 pass;
2575 else:
2576 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1),
2577 bytearray([0x31,0x32,0x33,0x34])), True);
2578 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1),
2579 bytearray([0x99,0x32,0x32,0x34])), False);
2580 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1),
2581 buffer(bytearray([0x31,0x32,0x33,0x34,0x34]), 0, 4)), True);
2582 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1),
2583 buffer(bytearray([0x99,0x32,0x33,0x34,0x34]), 0, 4)), False);
2584 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1), b'1234'), True);
2585 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1), '1234'), True);
2586 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1), u'1234'), True);
2587
2588if __name__ == '__main__':
2589 unittest.main();
2590 # 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