blob: 9d326ef1104d92e6240d605b925f9e0940d6a176 [file] [log] [blame]
Chris McDonaldc3e0f26b2020-06-04 02:15:141#!/usr/bin/env python
[email protected]0a88a652012-03-09 00:34:452# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Sets environment variables needed to run a chromium unit test."""
7
Chris McDonaldc3e0f26b2020-06-04 02:15:148from __future__ import print_function
Trent Aptedb1852432018-01-25 02:15:109import io
[email protected]0a88a652012-03-09 00:34:4510import os
Ben Pastenee5ece812018-06-20 22:39:3411import signal
[email protected]0a88a652012-03-09 00:34:4512import subprocess
13import sys
Trent Aptedb1852432018-01-25 02:15:1014import time
[email protected]0a88a652012-03-09 00:34:4515
Chris McDonaldc3e0f26b2020-06-04 02:15:1416
[email protected]0a88a652012-03-09 00:34:4517# This is hardcoded to be src/ relative to this script.
18ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
19
[email protected]76361be452012-08-30 22:48:1420CHROME_SANDBOX_ENV = 'CHROME_DEVEL_SANDBOX'
21CHROME_SANDBOX_PATH = '/opt/chromium/chrome_sandbox'
22
23
earthdok748e1352015-01-27 23:04:0824def get_sandbox_env(env):
25 """Returns the environment flags needed for the SUID sandbox to work."""
John Abd-El-Malek14ae0542014-10-15 17:52:3126 extra_env = {}
[email protected]76361be452012-08-30 22:48:1427 chrome_sandbox_path = env.get(CHROME_SANDBOX_ENV, CHROME_SANDBOX_PATH)
earthdok748e1352015-01-27 23:04:0828 # The above would silently disable the SUID sandbox if the env value were
29 # an empty string. We don't want to allow that. http://crbug.com/245376
30 # TODO(jln): Remove this check once it's no longer possible to disable the
31 # sandbox that way.
32 if not chrome_sandbox_path:
33 chrome_sandbox_path = CHROME_SANDBOX_PATH
34 extra_env[CHROME_SANDBOX_ENV] = chrome_sandbox_path
John Abd-El-Malek14ae0542014-10-15 17:52:3135
36 return extra_env
37
38
39def trim_cmd(cmd):
40 """Removes internal flags from cmd since they're just used to communicate from
41 the host machine to this script running on the swarm slaves."""
earthdokeeb065302015-02-04 18:18:0442 sanitizers = ['asan', 'lsan', 'msan', 'tsan']
43 internal_flags = frozenset('--%s=%d' % (name, value)
44 for name in sanitizers
45 for value in [0, 1])
John Abd-El-Malek14ae0542014-10-15 17:52:3146 return [i for i in cmd if i not in internal_flags]
[email protected]76361be452012-08-30 22:48:1447
[email protected]0a88a652012-03-09 00:34:4548
[email protected]4bf4d632012-05-31 15:50:3049def fix_python_path(cmd):
50 """Returns the fixed command line to call the right python executable."""
51 out = cmd[:]
52 if out[0] == 'python':
53 out[0] = sys.executable
54 elif out[0].endswith('.py'):
55 out.insert(0, sys.executable)
56 return out
57
58
pcc46233c22017-06-20 22:11:4159def get_sanitizer_env(cmd, asan, lsan, msan, tsan, cfi_diag):
earthdokeeb065302015-02-04 18:18:0460 """Returns the envirnoment flags needed for sanitizer tools."""
John Abd-El-Malek14ae0542014-10-15 17:52:3161
62 extra_env = {}
63
earthdokeeb065302015-02-04 18:18:0464 # Instruct GTK to use malloc while running sanitizer-instrumented tests.
earthdoke6c54a42014-10-16 18:02:3665 extra_env['G_SLICE'] = 'always-malloc'
John Abd-El-Malek14ae0542014-10-15 17:52:3166
67 extra_env['NSS_DISABLE_ARENA_FREE_LIST'] = '1'
68 extra_env['NSS_DISABLE_UNLOAD'] = '1'
69
70 # TODO(glider): remove the symbolizer path once
71 # https://code.google.com/p/address-sanitizer/issues/detail?id=134 is fixed.
Nico Weber19097312016-01-29 18:13:1572 symbolizer_path = os.path.join(ROOT_DIR,
73 'third_party', 'llvm-build', 'Release+Asserts', 'bin', 'llvm-symbolizer')
John Abd-El-Malek14ae0542014-10-15 17:52:3174
earthdokeeb065302015-02-04 18:18:0475 if lsan or tsan:
John Abd-El-Malek14ae0542014-10-15 17:52:3176 # LSan is not sandbox-compatible, so we can use online symbolization. In
77 # fact, it needs symbolization to be able to apply suppressions.
78 symbolization_options = ['symbolize=1',
79 'external_symbolizer_path=%s' % symbolizer_path]
pcc46233c22017-06-20 22:11:4180 elif (asan or msan or cfi_diag) and sys.platform not in ['win32', 'cygwin']:
timurrrr064f9522015-02-12 15:46:3281 # ASan uses a script for offline symbolization, except on Windows.
John Abd-El-Malek14ae0542014-10-15 17:52:3182 # Important note: when running ASan with leak detection enabled, we must use
83 # the LSan symbolization options above.
84 symbolization_options = ['symbolize=0']
vadimsh5cf3c442014-10-20 10:22:3885 # Set the path to llvm-symbolizer to be used by asan_symbolize.py
86 extra_env['LLVM_SYMBOLIZER_PATH'] = symbolizer_path
timurrrr7e67df92015-02-12 21:50:5287 else:
88 symbolization_options = []
John Abd-El-Malek14ae0542014-10-15 17:52:3189
Yves Gerey2e221522019-03-13 08:46:1990 # Leverage sanitizer to print stack trace on abort (e.g. assertion failure).
91 symbolization_options.append('handle_abort=1')
92
earthdokeeb065302015-02-04 18:18:0493 if asan:
94 asan_options = symbolization_options[:]
95 if lsan:
96 asan_options.append('detect_leaks=1')
Ben Pastene536f57c2021-05-17 23:21:5697 # LSan appears to have trouble with later versions of glibc.
98 # See https://github.com/google/sanitizers/issues/1322
99 if 'linux' in sys.platform:
100 asan_options.append('intercept_tls_get_addr=0')
John Abd-El-Malek14ae0542014-10-15 17:52:31101
timurrrr7e67df92015-02-12 21:50:52102 if asan_options:
103 extra_env['ASAN_OPTIONS'] = ' '.join(asan_options)
John Abd-El-Malek14ae0542014-10-15 17:52:31104
earthdokeeb065302015-02-04 18:18:04105 if sys.platform == 'darwin':
106 isolate_output_dir = os.path.abspath(os.path.dirname(cmd[0]))
107 # This is needed because the test binary has @executable_path embedded in
108 # it that the OS tries to resolve to the cache directory and not the
109 # mapped directory.
110 extra_env['DYLD_LIBRARY_PATH'] = str(isolate_output_dir)
111
112 if lsan:
113 if asan or msan:
114 lsan_options = []
115 else:
116 lsan_options = symbolization_options[:]
117 if sys.platform == 'linux2':
118 # Use the debug version of libstdc++ under LSan. If we don't, there will
119 # be a lot of incomplete stack traces in the reports.
120 extra_env['LD_LIBRARY_PATH'] = '/usr/lib/x86_64-linux-gnu/debug:'
121
earthdokeeb065302015-02-04 18:18:04122 extra_env['LSAN_OPTIONS'] = ' '.join(lsan_options)
123
124 if msan:
125 msan_options = symbolization_options[:]
126 if lsan:
127 msan_options.append('detect_leaks=1')
128 extra_env['MSAN_OPTIONS'] = ' '.join(msan_options)
129
130 if tsan:
131 tsan_options = symbolization_options[:]
132 extra_env['TSAN_OPTIONS'] = ' '.join(tsan_options)
John Abd-El-Malek14ae0542014-10-15 17:52:31133
pcc46233c22017-06-20 22:11:41134 # CFI uses the UBSan runtime to provide diagnostics.
135 if cfi_diag:
136 ubsan_options = symbolization_options[:] + ['print_stacktrace=1']
137 extra_env['UBSAN_OPTIONS'] = ' '.join(ubsan_options)
138
John Abd-El-Malek14ae0542014-10-15 17:52:31139 return extra_env
140
141
glider9d919342015-02-06 17:42:27142def get_sanitizer_symbolize_command(json_path=None, executable_path=None):
earthdok9bd5d582015-01-29 21:19:36143 """Construct the command to invoke offline symbolization script."""
Nico Weber19097312016-01-29 18:13:15144 script_path = os.path.join(
145 ROOT_DIR, 'tools', 'valgrind', 'asan', 'asan_symbolize.py')
earthdok9bd5d582015-01-29 21:19:36146 cmd = [sys.executable, script_path]
147 if json_path is not None:
148 cmd.append('--test-summary-json-file=%s' % json_path)
glider9d919342015-02-06 17:42:27149 if executable_path is not None:
150 cmd.append('--executable-path=%s' % executable_path)
earthdok9bd5d582015-01-29 21:19:36151 return cmd
152
153
154def get_json_path(cmd):
155 """Extract the JSON test summary path from a command line."""
156 json_path_flag = '--test-launcher-summary-output='
157 for arg in cmd:
158 if arg.startswith(json_path_flag):
159 return arg.split(json_path_flag).pop()
160 return None
161
162
163def symbolize_snippets_in_json(cmd, env):
164 """Symbolize output snippets inside the JSON test summary."""
165 json_path = get_json_path(cmd)
166 if json_path is None:
167 return
168
169 try:
glider9d919342015-02-06 17:42:27170 symbolize_command = get_sanitizer_symbolize_command(
171 json_path=json_path, executable_path=cmd[0])
earthdok9bd5d582015-01-29 21:19:36172 p = subprocess.Popen(symbolize_command, stderr=subprocess.PIPE, env=env)
173 (_, stderr) = p.communicate()
174 except OSError as e:
Chris McDonaldc3e0f26b2020-06-04 02:15:14175 print('Exception while symbolizing snippets: %s' % e, file=sys.stderr)
thakis45643b42016-01-29 21:53:42176 raise
earthdok9bd5d582015-01-29 21:19:36177
178 if p.returncode != 0:
Chris McDonaldc3e0f26b2020-06-04 02:15:14179 print("Error: failed to symbolize snippets in JSON:\n", file=sys.stderr)
180 print(stderr, file=sys.stderr)
thakis45643b42016-01-29 21:53:42181 raise subprocess.CalledProcessError(p.returncode, symbolize_command)
earthdok9bd5d582015-01-29 21:19:36182
183
Trent Aptedb1852432018-01-25 02:15:10184def run_command_with_output(argv, stdoutfile, env=None, cwd=None):
Caleb Rouleaufd511292019-02-22 18:22:00185 """Run command and stream its stdout/stderr to the console & |stdoutfile|.
186
187 Also forward_signals to obey
John Palmera8515fca2021-05-20 03:35:32188 https://chromium.googlesource.com/infra/luci/luci-py/+/main/appengine/swarming/doc/Bot.md#graceful-termination_aka-the-sigterm-and-sigkill-dance
Caleb Rouleaufd511292019-02-22 18:22:00189
190 Returns:
191 integer returncode of the subprocess.
Trent Aptedb1852432018-01-25 02:15:10192 """
Sorin Jianue11c9852019-09-12 20:43:55193 print('Running %r in %r (env: %r)' % (argv, cwd, env))
Trent Aptedb1852432018-01-25 02:15:10194 assert stdoutfile
Ned Nguyen7f43ff62018-10-04 04:58:46195 with io.open(stdoutfile, 'wb') as writer, \
196 io.open(stdoutfile, 'rb', 1) as reader:
Caleb Rouleau6844df152019-09-11 01:11:59197 process = _popen(argv, env=env, cwd=cwd, stdout=writer,
198 stderr=subprocess.STDOUT)
Ben Pastenee5ece812018-06-20 22:39:34199 forward_signals([process])
Trent Aptedb1852432018-01-25 02:15:10200 while process.poll() is None:
201 sys.stdout.write(reader.read())
Caleb Rouleaufd511292019-02-22 18:22:00202 # This sleep is needed for signal propagation. See the
203 # wait_with_signals() docstring.
Trent Aptedb1852432018-01-25 02:15:10204 time.sleep(0.1)
205 # Read the remaining.
206 sys.stdout.write(reader.read())
Sorin Jianue11c9852019-09-12 20:43:55207 print('Command %r returned exit code %d' % (argv, process.returncode))
Trent Aptedb1852432018-01-25 02:15:10208 return process.returncode
209
210
Caleb Rouleaufd511292019-02-22 18:22:00211def run_command(argv, env=None, cwd=None, log=True):
212 """Run command and stream its stdout/stderr both to stdout.
213
214 Also forward_signals to obey
John Palmera8515fca2021-05-20 03:35:32215 https://chromium.googlesource.com/infra/luci/luci-py/+/main/appengine/swarming/doc/Bot.md#graceful-termination_aka-the-sigterm-and-sigkill-dance
Caleb Rouleaufd511292019-02-22 18:22:00216
217 Returns:
218 integer returncode of the subprocess.
219 """
220 if log:
Sorin Jianue11c9852019-09-12 20:43:55221 print('Running %r in %r (env: %r)' % (argv, cwd, env))
Caleb Rouleau6844df152019-09-11 01:11:59222 process = _popen(argv, env=env, cwd=cwd, stderr=subprocess.STDOUT)
Caleb Rouleaufd511292019-02-22 18:22:00223 forward_signals([process])
224 return wait_with_signals(process)
225
226
Caleb Rouleau84e3e812019-05-30 23:34:50227def run_command_output_to_handle(argv, file_handle, env=None, cwd=None):
228 """Run command and stream its stdout/stderr both to |file_handle|.
229
230 Also forward_signals to obey
John Palmera8515fca2021-05-20 03:35:32231 https://chromium.googlesource.com/infra/luci/luci-py/+/main/appengine/swarming/doc/Bot.md#graceful-termination_aka-the-sigterm-and-sigkill-dance
Caleb Rouleau84e3e812019-05-30 23:34:50232
233 Returns:
234 integer returncode of the subprocess.
235 """
Sorin Jianue11c9852019-09-12 20:43:55236 print('Running %r in %r (env: %r)' % (argv, cwd, env))
Caleb Rouleau6844df152019-09-11 01:11:59237 process = _popen(
Caleb Rouleau84e3e812019-05-30 23:34:50238 argv, env=env, cwd=cwd, stderr=file_handle, stdout=file_handle)
239 forward_signals([process])
240 exit_code = wait_with_signals(process)
Sorin Jianue11c9852019-09-12 20:43:55241 print('Command returned exit code %d' % exit_code)
Caleb Rouleau84e3e812019-05-30 23:34:50242 return exit_code
243
244
Caleb Rouleaufd511292019-02-22 18:22:00245def wait_with_signals(process):
246 """A version of process.wait() that works cross-platform.
247
248 This version properly surfaces the SIGBREAK signal.
249
250 From reading the subprocess.py source code, it seems we need to explicitly
251 call time.sleep(). The reason is that subprocess.Popen.wait() on Windows
252 directly calls WaitForSingleObject(), but only time.sleep() properly surface
253 the SIGBREAK signal.
254
255 Refs:
256 https://github.com/python/cpython/blob/v2.7.15/Lib/subprocess.py#L692
257 https://github.com/python/cpython/blob/v2.7.15/Modules/timemodule.c#L1084
258
259 Returns:
260 returncode of the process.
261 """
262 while process.poll() is None:
263 time.sleep(0.1)
264 return process.returncode
265
266
Ben Pastenee5ece812018-06-20 22:39:34267def forward_signals(procs):
268 """Forwards unix's SIGTERM or win's CTRL_BREAK_EVENT to the given processes.
269
270 This plays nicely with swarming's timeout handling. See also
John Palmera8515fca2021-05-20 03:35:32271 https://chromium.googlesource.com/infra/luci/luci-py/+/main/appengine/swarming/doc/Bot.md#graceful-termination_aka-the-sigterm-and-sigkill-dance
Ben Pastenee5ece812018-06-20 22:39:34272
273 Args:
274 procs: A list of subprocess.Popen objects representing child processes.
275 """
276 assert all(isinstance(p, subprocess.Popen) for p in procs)
277 def _sig_handler(sig, _):
278 for p in procs:
Ilia Samsonova00835302019-04-19 17:37:59279 if p.poll() is not None:
280 continue
Andrew Grieve95a2d332018-06-28 02:22:59281 # SIGBREAK is defined only for win32.
282 if sys.platform == 'win32' and sig == signal.SIGBREAK:
Ben Pastenee5ece812018-06-20 22:39:34283 p.send_signal(signal.CTRL_BREAK_EVENT)
284 else:
285 p.send_signal(sig)
286 if sys.platform == 'win32':
287 signal.signal(signal.SIGBREAK, _sig_handler)
288 else:
289 signal.signal(signal.SIGTERM, _sig_handler)
Ilia Samsonova00835302019-04-19 17:37:59290 signal.signal(signal.SIGINT, _sig_handler)
Ben Pastenee5ece812018-06-20 22:39:34291
Haiyang Pan94c26592019-08-13 17:48:11292
Trent Aptedb1852432018-01-25 02:15:10293def run_executable(cmd, env, stdoutfile=None):
[email protected]0a88a652012-03-09 00:34:45294 """Runs an executable with:
Dirk Pranke50e557b2017-12-01 23:48:09295 - CHROME_HEADLESS set to indicate that the test is running on a
296 bot and shouldn't do anything interactive like show modal dialogs.
[email protected]3766ed1c2012-07-26 20:53:56297 - environment variable CR_SOURCE_ROOT set to the root directory.
[email protected]0a88a652012-03-09 00:34:45298 - environment variable LANGUAGE to en_US.UTF-8.
earthdok748e1352015-01-27 23:04:08299 - environment variable CHROME_DEVEL_SANDBOX set
[email protected]0a88a652012-03-09 00:34:45300 - Reuses sys.executable automatically.
301 """
Dirk Pranke50e557b2017-12-01 23:48:09302 extra_env = {
303 # Set to indicate that the executable is running non-interactively on
304 # a bot.
305 'CHROME_HEADLESS': '1',
306
307 # Many tests assume a English interface...
308 'LANG': 'en_US.UTF-8',
309 }
310
[email protected]3766ed1c2012-07-26 20:53:56311 # Used by base/base_paths_linux.cc as an override. Just make sure the default
312 # logic is used.
313 env.pop('CR_SOURCE_ROOT', None)
earthdok748e1352015-01-27 23:04:08314 extra_env.update(get_sandbox_env(env))
John Abd-El-Malek4569c422014-10-09 05:10:53315
316 # Copy logic from tools/build/scripts/slave/runtest.py.
317 asan = '--asan=1' in cmd
318 lsan = '--lsan=1' in cmd
earthdokeeb065302015-02-04 18:18:04319 msan = '--msan=1' in cmd
320 tsan = '--tsan=1' in cmd
pcc46233c22017-06-20 22:11:41321 cfi_diag = '--cfi-diag=1' in cmd
Trent Aptedb1852432018-01-25 02:15:10322 if stdoutfile or sys.platform in ['win32', 'cygwin']:
timurrrr064f9522015-02-12 15:46:32323 # Symbolization works in-process on Windows even when sandboxed.
324 use_symbolization_script = False
325 else:
Ilia Samsonov4ea016762019-12-02 22:11:13326 # If any sanitizer is enabled, we print unsymbolized stack trace
327 # that is required to run through symbolization script.
328 use_symbolization_script = (asan or msan or cfi_diag or lsan or tsan)
John Abd-El-Malek4569c422014-10-09 05:10:53329
pcc46233c22017-06-20 22:11:41330 if asan or lsan or msan or tsan or cfi_diag:
331 extra_env.update(get_sanitizer_env(cmd, asan, lsan, msan, tsan, cfi_diag))
earthdokeeb065302015-02-04 18:18:04332
Timur Iskhodzhanovfdbfd4e12015-02-05 13:50:23333 if lsan or tsan:
334 # LSan and TSan are not sandbox-friendly.
335 cmd.append('--no-sandbox')
John Abd-El-Malek14ae0542014-10-15 17:52:31336
337 cmd = trim_cmd(cmd)
John Abd-El-Malek4569c422014-10-09 05:10:53338
[email protected]8ba98352012-05-23 20:43:59339 # Ensure paths are correctly separated on windows.
340 cmd[0] = cmd[0].replace('/', os.path.sep)
[email protected]4bf4d632012-05-31 15:50:30341 cmd = fix_python_path(cmd)
John Abd-El-Malek14ae0542014-10-15 17:52:31342
Dirk Pranke2cf1a152019-04-03 01:34:38343 # We also want to print the GTEST env vars that were set by the caller,
344 # because you need them to reproduce the task properly.
345 env_to_print = extra_env.copy()
346 for env_var_name in ('GTEST_SHARD_INDEX', 'GTEST_TOTAL_SHARDS'):
Chris McDonaldc3e0f26b2020-06-04 02:15:14347 if env_var_name in env:
348 env_to_print[env_var_name] = env[env_var_name]
Dirk Pranke2cf1a152019-04-03 01:34:38349
Sorin Jianue11c9852019-09-12 20:43:55350 print('Additional test environment:\n%s\n'
John Abd-El-Malek14ae0542014-10-15 17:52:31351 'Command: %s\n' % (
Dirk Pranke2b9bec72020-11-18 04:33:38352 '\n'.join(' %s=%s' % (k, v)
353 for k, v in sorted(env_to_print.items())),
Sorin Jianue11c9852019-09-12 20:43:55354 ' '.join(cmd)))
maruelf00125f82016-11-19 00:01:14355 sys.stdout.flush()
John Abd-El-Malek14ae0542014-10-15 17:52:31356 env.update(extra_env or {})
[email protected]50ec9f232012-03-16 04:18:23357 try:
Trent Aptedb1852432018-01-25 02:15:10358 if stdoutfile:
359 # Write to stdoutfile and poll to produce terminal output.
360 return run_command_with_output(cmd, env=env, stdoutfile=stdoutfile)
361 elif use_symbolization_script:
362 # See above comment regarding offline symbolization.
John Abd-El-Malek4569c422014-10-09 05:10:53363 # Need to pipe to the symbolizer script.
Caleb Rouleau6844df152019-09-11 01:11:59364 p1 = _popen(cmd, env=env, stdout=subprocess.PIPE,
365 stderr=sys.stdout)
366 p2 = _popen(
glider9d919342015-02-06 17:42:27367 get_sanitizer_symbolize_command(executable_path=cmd[0]),
368 env=env, stdin=p1.stdout)
John Abd-El-Malek4569c422014-10-09 05:10:53369 p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
Ben Pastenee5ece812018-06-20 22:39:34370 forward_signals([p1, p2])
Caleb Rouleaufd511292019-02-22 18:22:00371 wait_with_signals(p1)
372 wait_with_signals(p2)
earthdok9bd5d582015-01-29 21:19:36373 # Also feed the out-of-band JSON output to the symbolizer script.
374 symbolize_snippets_in_json(cmd, env)
vadimsh1eaeb2982014-10-20 12:28:45375 return p1.returncode
John Abd-El-Malek4569c422014-10-09 05:10:53376 else:
Caleb Rouleaufd511292019-02-22 18:22:00377 return run_command(cmd, env=env, log=False)
[email protected]50ec9f232012-03-16 04:18:23378 except OSError:
Chris McDonaldc3e0f26b2020-06-04 02:15:14379 print('Failed to start %s' % cmd, file=sys.stderr)
[email protected]50ec9f232012-03-16 04:18:23380 raise
[email protected]0a88a652012-03-09 00:34:45381
382
Caleb Rouleau6844df152019-09-11 01:11:59383def _popen(*args, **kwargs):
384 assert 'creationflags' not in kwargs
385 if sys.platform == 'win32':
386 # Necessary for signal handling. See crbug.com/733612#c6.
387 kwargs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP
388 return subprocess.Popen(*args, **kwargs)
389
390
[email protected]0a88a652012-03-09 00:34:45391def main():
gliderb73f1872014-10-09 16:24:56392 return run_executable(sys.argv[1:], os.environ.copy())
[email protected]0a88a652012-03-09 00:34:45393
394
[email protected]ed763a72012-08-29 03:51:22395if __name__ == '__main__':
Will Harris9e4cf312021-04-19 23:55:15396 if sys.platform == 'win32':
Greg Thompson0c98a682021-06-05 03:30:44397 sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
Will Harris9e4cf312021-04-19 23:55:15398 'scripts'))
399 import common
400 common.set_lpac_acls(ROOT_DIR)
[email protected]0a88a652012-03-09 00:34:45401 sys.exit(main())