blob: 26d421775405cd46d97f017596392a13b6d9958d [file] [log] [blame]
mbligh63073c92008-03-31 16:49:32 +00001#!/usr/bin/python
2#
3# Copyright 2008 Google Inc. Released under the GPL v2
4
mbligh849a0f62008-08-28 20:12:19 +00005import os, pickle, random, re, resource, select, shutil, signal, StringIO
mblighb2896192009-07-11 00:12:37 +00006import socket, struct, subprocess, sys, time, textwrap, urlparse
mbligh25284cd2009-06-08 16:17:24 +00007import warnings, smtplib, logging, urllib2
showard108d73e2009-06-22 18:14:41 +00008from autotest_lib.client.common_lib import error, barrier, logging_manager
mbligh81edd792008-08-26 16:54:02 +00009
mbligh849a0f62008-08-28 20:12:19 +000010def deprecated(func):
11 """This is a decorator which can be used to mark functions as deprecated.
12 It will result in a warning being emmitted when the function is used."""
13 def new_func(*args, **dargs):
14 warnings.warn("Call to deprecated function %s." % func.__name__,
15 category=DeprecationWarning)
16 return func(*args, **dargs)
17 new_func.__name__ = func.__name__
18 new_func.__doc__ = func.__doc__
19 new_func.__dict__.update(func.__dict__)
20 return new_func
21
22
showard108d73e2009-06-22 18:14:41 +000023class _NullStream(object):
24 def write(self, data):
25 pass
26
27
28 def flush(self):
29 pass
30
31
32TEE_TO_LOGS = object()
33_the_null_stream = _NullStream()
34
35def get_stream_tee_file(stream, level):
36 if stream is None:
37 return _the_null_stream
38 if stream is TEE_TO_LOGS:
39 return logging_manager.LoggingFile(level=level)
40 return stream
41
42
mbligh849a0f62008-08-28 20:12:19 +000043class BgJob(object):
showard170873e2009-01-07 00:22:26 +000044 def __init__(self, command, stdout_tee=None, stderr_tee=None, verbose=True,
45 stdin=None):
mbligh849a0f62008-08-28 20:12:19 +000046 self.command = command
showard108d73e2009-06-22 18:14:41 +000047 self.stdout_tee = get_stream_tee_file(stdout_tee, logging.DEBUG)
48 self.stderr_tee = get_stream_tee_file(stderr_tee, logging.ERROR)
mbligh849a0f62008-08-28 20:12:19 +000049 self.result = CmdResult(command)
mblighbd96b452008-09-03 23:14:27 +000050 if verbose:
showardb18134f2009-03-20 20:52:18 +000051 logging.debug("Running '%s'" % command)
mbligh849a0f62008-08-28 20:12:19 +000052 self.sp = subprocess.Popen(command, stdout=subprocess.PIPE,
53 stderr=subprocess.PIPE,
54 preexec_fn=self._reset_sigpipe, shell=True,
showard170873e2009-01-07 00:22:26 +000055 executable="/bin/bash",
56 stdin=stdin)
mbligh849a0f62008-08-28 20:12:19 +000057
58
59 def output_prepare(self, stdout_file=None, stderr_file=None):
60 self.stdout_file = stdout_file
61 self.stderr_file = stderr_file
62
mbligh45ffc432008-12-09 23:35:17 +000063
mbligh849a0f62008-08-28 20:12:19 +000064 def process_output(self, stdout=True, final_read=False):
65 """output_prepare must be called prior to calling this"""
66 if stdout:
67 pipe, buf, tee = self.sp.stdout, self.stdout_file, self.stdout_tee
68 else:
69 pipe, buf, tee = self.sp.stderr, self.stderr_file, self.stderr_tee
70
71 if final_read:
72 # read in all the data we can from pipe and then stop
73 data = []
74 while select.select([pipe], [], [], 0)[0]:
75 data.append(os.read(pipe.fileno(), 1024))
76 if len(data[-1]) == 0:
77 break
78 data = "".join(data)
79 else:
80 # perform a single read
81 data = os.read(pipe.fileno(), 1024)
82 buf.write(data)
showard108d73e2009-06-22 18:14:41 +000083 tee.write(data)
mbligh849a0f62008-08-28 20:12:19 +000084
85
86 def cleanup(self):
showard108d73e2009-06-22 18:14:41 +000087 self.stdout_tee.flush()
88 self.stderr_tee.flush()
mbligh849a0f62008-08-28 20:12:19 +000089 self.sp.stdout.close()
90 self.sp.stderr.close()
91 self.result.stdout = self.stdout_file.getvalue()
92 self.result.stderr = self.stderr_file.getvalue()
93
94
95 def _reset_sigpipe(self):
96 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
97
mbligh81edd792008-08-26 16:54:02 +000098
99def ip_to_long(ip):
100 # !L is a long in network byte order
101 return struct.unpack('!L', socket.inet_aton(ip))[0]
102
103
104def long_to_ip(number):
105 # See above comment.
106 return socket.inet_ntoa(struct.pack('!L', number))
107
108
109def create_subnet_mask(bits):
mbligh81edd792008-08-26 16:54:02 +0000110 return (1 << 32) - (1 << 32-bits)
111
112
113def format_ip_with_mask(ip, mask_bits):
114 masked_ip = ip_to_long(ip) & create_subnet_mask(mask_bits)
115 return "%s/%s" % (long_to_ip(masked_ip), mask_bits)
mbligh6231cd62008-02-02 19:18:33 +0000116
mblighde0d47e2008-03-28 14:37:18 +0000117
jadmanskie80d4712008-10-03 16:15:59 +0000118def normalize_hostname(alias):
119 ip = socket.gethostbyname(alias)
120 return socket.gethostbyaddr(ip)[0]
121
122
mblighd6d043c2008-09-27 21:00:45 +0000123def get_ip_local_port_range():
124 match = re.match(r'\s*(\d+)\s*(\d+)\s*$',
125 read_one_line('/proc/sys/net/ipv4/ip_local_port_range'))
126 return (int(match.group(1)), int(match.group(2)))
127
128
129def set_ip_local_port_range(lower, upper):
130 write_one_line('/proc/sys/net/ipv4/ip_local_port_range',
131 '%d %d\n' % (lower, upper))
132
mbligh315b9412008-10-01 03:34:11 +0000133
mbligh45ffc432008-12-09 23:35:17 +0000134
135def send_email(mail_from, mail_to, subject, body):
136 """
137 Sends an email via smtp
138
139 mail_from: string with email address of sender
140 mail_to: string or list with email address(es) of recipients
141 subject: string with subject of email
142 body: (multi-line) string with body of email
143 """
144 if isinstance(mail_to, str):
145 mail_to = [mail_to]
146 msg = "From: %s\nTo: %s\nSubject: %s\n\n%s" % (mail_from, ','.join(mail_to),
147 subject, body)
148 try:
149 mailer = smtplib.SMTP('localhost')
150 try:
151 mailer.sendmail(mail_from, mail_to, msg)
152 finally:
153 mailer.quit()
154 except Exception, e:
155 # Emails are non-critical, not errors, but don't raise them
156 print "Sending email failed. Reason: %s" % repr(e)
157
158
jadmanski5182e162008-05-13 21:48:16 +0000159def read_one_line(filename):
mbligh6e8840c2008-07-11 18:05:49 +0000160 return open(filename, 'r').readline().rstrip('\n')
jadmanski5182e162008-05-13 21:48:16 +0000161
162
mblighb9d05512008-10-18 13:53:27 +0000163def write_one_line(filename, line):
164 open_write_close(filename, line.rstrip('\n') + '\n')
165
166
167def open_write_close(filename, data):
mbligh618ac9e2008-10-06 17:14:32 +0000168 f = open(filename, 'w')
mblighb9d05512008-10-18 13:53:27 +0000169 try:
170 f.write(data)
171 finally:
172 f.close()
jadmanski5182e162008-05-13 21:48:16 +0000173
174
mblighde0d47e2008-03-28 14:37:18 +0000175def read_keyval(path):
jadmanski0afbb632008-06-06 21:10:57 +0000176 """
177 Read a key-value pair format file into a dictionary, and return it.
178 Takes either a filename or directory name as input. If it's a
179 directory name, we assume you want the file to be called keyval.
180 """
181 if os.path.isdir(path):
182 path = os.path.join(path, 'keyval')
183 keyval = {}
jadmanski58962982009-04-21 19:54:34 +0000184 if os.path.exists(path):
185 for line in open(path):
186 line = re.sub('#.*', '', line).rstrip()
187 if not re.search(r'^[-\.\w]+=', line):
188 raise ValueError('Invalid format line: %s' % line)
189 key, value = line.split('=', 1)
190 if re.search('^\d+$', value):
191 value = int(value)
192 elif re.search('^(\d+\.)?\d+$', value):
193 value = float(value)
194 keyval[key] = value
jadmanski0afbb632008-06-06 21:10:57 +0000195 return keyval
mblighde0d47e2008-03-28 14:37:18 +0000196
197
jadmanskicc549172008-05-21 18:11:51 +0000198def write_keyval(path, dictionary, type_tag=None):
jadmanski0afbb632008-06-06 21:10:57 +0000199 """
200 Write a key-value pair format file out to a file. This uses append
201 mode to open the file, so existing text will not be overwritten or
202 reparsed.
jadmanskicc549172008-05-21 18:11:51 +0000203
jadmanski0afbb632008-06-06 21:10:57 +0000204 If type_tag is None, then the key must be composed of alphanumeric
205 characters (or dashes+underscores). However, if type-tag is not
206 null then the keys must also have "{type_tag}" as a suffix. At
207 the moment the only valid values of type_tag are "attr" and "perf".
208 """
209 if os.path.isdir(path):
210 path = os.path.join(path, 'keyval')
211 keyval = open(path, 'a')
jadmanskicc549172008-05-21 18:11:51 +0000212
jadmanski0afbb632008-06-06 21:10:57 +0000213 if type_tag is None:
mbligh97227ea2009-03-11 17:09:50 +0000214 key_regex = re.compile(r'^[-\.\w]+$')
jadmanski0afbb632008-06-06 21:10:57 +0000215 else:
216 if type_tag not in ('attr', 'perf'):
217 raise ValueError('Invalid type tag: %s' % type_tag)
218 escaped_tag = re.escape(type_tag)
mbligh97227ea2009-03-11 17:09:50 +0000219 key_regex = re.compile(r'^[-\.\w]+\{%s\}$' % escaped_tag)
jadmanski0afbb632008-06-06 21:10:57 +0000220 try:
mbligh6955e232009-07-11 00:58:47 +0000221 for key in sorted(dictionary.keys()):
jadmanski0afbb632008-06-06 21:10:57 +0000222 if not key_regex.search(key):
223 raise ValueError('Invalid key: %s' % key)
mbligh6955e232009-07-11 00:58:47 +0000224 keyval.write('%s=%s\n' % (key, dictionary[key]))
jadmanski0afbb632008-06-06 21:10:57 +0000225 finally:
226 keyval.close()
mbligh6231cd62008-02-02 19:18:33 +0000227
228
229def is_url(path):
jadmanski0afbb632008-06-06 21:10:57 +0000230 """Return true if path looks like a URL"""
231 # for now, just handle http and ftp
232 url_parts = urlparse.urlparse(path)
233 return (url_parts[0] in ('http', 'ftp'))
mbligh6231cd62008-02-02 19:18:33 +0000234
235
mblighb2896192009-07-11 00:12:37 +0000236def urlopen(url, data=None, timeout=5):
237 """Wrapper to urllib2.urlopen with timeout addition."""
mbligh02ff2d52008-06-03 15:00:21 +0000238
jadmanski0afbb632008-06-06 21:10:57 +0000239 # Save old timeout
240 old_timeout = socket.getdefaulttimeout()
241 socket.setdefaulttimeout(timeout)
242 try:
mblighb2896192009-07-11 00:12:37 +0000243 return urllib2.urlopen(url, data=data)
jadmanski0afbb632008-06-06 21:10:57 +0000244 finally:
245 socket.setdefaulttimeout(old_timeout)
mbligh02ff2d52008-06-03 15:00:21 +0000246
247
mblighb2896192009-07-11 00:12:37 +0000248def urlretrieve(url, filename, data=None, timeout=300):
249 """Retrieve a file from given url."""
250 logging.debug('Fetching %s -> %s', url, filename)
251
252 src_file = urlopen(url, data=data, timeout=timeout)
jadmanski0afbb632008-06-06 21:10:57 +0000253 try:
mblighb2896192009-07-11 00:12:37 +0000254 dest_file = open(filename, 'wb')
255 try:
256 shutil.copyfileobj(src_file, dest_file)
257 finally:
258 dest_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000259 finally:
mblighb2896192009-07-11 00:12:37 +0000260 src_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000261
mbligh02ff2d52008-06-03 15:00:21 +0000262
mbligh6231cd62008-02-02 19:18:33 +0000263def get_file(src, dest, permissions=None):
jadmanski0afbb632008-06-06 21:10:57 +0000264 """Get a file from src, which can be local or a remote URL"""
mbligh25284cd2009-06-08 16:17:24 +0000265 if src == dest:
jadmanski0afbb632008-06-06 21:10:57 +0000266 return
mbligh25284cd2009-06-08 16:17:24 +0000267
268 if is_url(src):
mblighb2896192009-07-11 00:12:37 +0000269 urlretrieve(src, dest)
jadmanski0afbb632008-06-06 21:10:57 +0000270 else:
271 shutil.copyfile(src, dest)
mbligh25284cd2009-06-08 16:17:24 +0000272
jadmanski0afbb632008-06-06 21:10:57 +0000273 if permissions:
274 os.chmod(dest, permissions)
275 return dest
mbligh6231cd62008-02-02 19:18:33 +0000276
277
278def unmap_url(srcdir, src, destdir='.'):
jadmanski0afbb632008-06-06 21:10:57 +0000279 """
280 Receives either a path to a local file or a URL.
281 returns either the path to the local file, or the fetched URL
mbligh6231cd62008-02-02 19:18:33 +0000282
jadmanski0afbb632008-06-06 21:10:57 +0000283 unmap_url('/usr/src', 'foo.tar', '/tmp')
284 = '/usr/src/foo.tar'
285 unmap_url('/usr/src', 'http://site/file', '/tmp')
286 = '/tmp/file'
287 (after retrieving it)
288 """
289 if is_url(src):
290 url_parts = urlparse.urlparse(src)
291 filename = os.path.basename(url_parts[2])
292 dest = os.path.join(destdir, filename)
293 return get_file(src, dest)
294 else:
295 return os.path.join(srcdir, src)
mbligh6231cd62008-02-02 19:18:33 +0000296
297
298def update_version(srcdir, preserve_srcdir, new_version, install,
jadmanski0afbb632008-06-06 21:10:57 +0000299 *args, **dargs):
300 """
301 Make sure srcdir is version new_version
mbligh6231cd62008-02-02 19:18:33 +0000302
jadmanski0afbb632008-06-06 21:10:57 +0000303 If not, delete it and install() the new version.
mbligh6231cd62008-02-02 19:18:33 +0000304
jadmanski0afbb632008-06-06 21:10:57 +0000305 In the preserve_srcdir case, we just check it's up to date,
306 and if not, we rerun install, without removing srcdir
307 """
308 versionfile = os.path.join(srcdir, '.version')
309 install_needed = True
mbligh6231cd62008-02-02 19:18:33 +0000310
jadmanski0afbb632008-06-06 21:10:57 +0000311 if os.path.exists(versionfile):
312 old_version = pickle.load(open(versionfile))
313 if old_version == new_version:
314 install_needed = False
mbligh6231cd62008-02-02 19:18:33 +0000315
jadmanski0afbb632008-06-06 21:10:57 +0000316 if install_needed:
317 if not preserve_srcdir and os.path.exists(srcdir):
318 shutil.rmtree(srcdir)
319 install(*args, **dargs)
320 if os.path.exists(srcdir):
321 pickle.dump(new_version, open(versionfile, 'w'))
mbligh462c0152008-03-13 15:37:10 +0000322
323
mbligh63073c92008-03-31 16:49:32 +0000324def run(command, timeout=None, ignore_status=False,
showard170873e2009-01-07 00:22:26 +0000325 stdout_tee=None, stderr_tee=None, verbose=True, stdin=None):
jadmanski0afbb632008-06-06 21:10:57 +0000326 """
327 Run a command on the host.
mbligh63073c92008-03-31 16:49:32 +0000328
jadmanski0afbb632008-06-06 21:10:57 +0000329 Args:
330 command: the command line string
331 timeout: time limit in seconds before attempting to
332 kill the running process. The run() function
333 will take a few seconds longer than 'timeout'
334 to complete if it has to kill the process.
335 ignore_status: do not raise an exception, no matter what
336 the exit code of the command is.
337 stdout_tee: optional file-like object to which stdout data
338 will be written as it is generated (data will still
339 be stored in result.stdout)
340 stderr_tee: likewise for stderr
showard108d73e2009-06-22 18:14:41 +0000341 verbose: if True, log the command being run
showard170873e2009-01-07 00:22:26 +0000342 stdin: stdin to pass to the executed process
mbligh63073c92008-03-31 16:49:32 +0000343
jadmanski0afbb632008-06-06 21:10:57 +0000344 Returns:
345 a CmdResult object
mbligh63073c92008-03-31 16:49:32 +0000346
jadmanski0afbb632008-06-06 21:10:57 +0000347 Raises:
348 CmdError: the exit code of the command
349 execution was not 0
350 """
showard170873e2009-01-07 00:22:26 +0000351 bg_job = join_bg_jobs(
352 (BgJob(command, stdout_tee, stderr_tee, verbose, stdin=stdin),),
353 timeout)[0]
mbligh849a0f62008-08-28 20:12:19 +0000354 if not ignore_status and bg_job.result.exit_status:
jadmanski9c1098b2008-09-02 14:18:48 +0000355 raise error.CmdError(command, bg_job.result,
mbligh849a0f62008-08-28 20:12:19 +0000356 "Command returned non-zero exit status")
mbligh63073c92008-03-31 16:49:32 +0000357
mbligh849a0f62008-08-28 20:12:19 +0000358 return bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000359
mbligh45ffc432008-12-09 23:35:17 +0000360
mbligha5630a52008-09-03 22:09:50 +0000361def run_parallel(commands, timeout=None, ignore_status=False,
362 stdout_tee=None, stderr_tee=None):
363 """Beahves the same as run with the following exceptions:
364
365 - commands is a list of commands to run in parallel.
366 - ignore_status toggles whether or not an exception should be raised
367 on any error.
368
369 returns a list of CmdResult objects
370 """
371 bg_jobs = []
372 for command in commands:
373 bg_jobs.append(BgJob(command, stdout_tee, stderr_tee))
374
375 # Updates objects in bg_jobs list with their process information
376 join_bg_jobs(bg_jobs, timeout)
377
378 for bg_job in bg_jobs:
379 if not ignore_status and bg_job.result.exit_status:
380 raise error.CmdError(command, bg_job.result,
381 "Command returned non-zero exit status")
382
383 return [bg_job.result for bg_job in bg_jobs]
384
385
mbligh849a0f62008-08-28 20:12:19 +0000386@deprecated
mbligh63073c92008-03-31 16:49:32 +0000387def run_bg(command):
mbligh849a0f62008-08-28 20:12:19 +0000388 """Function deprecated. Please use BgJob class instead."""
389 bg_job = BgJob(command)
390 return bg_job.sp, bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000391
392
mbligh849a0f62008-08-28 20:12:19 +0000393def join_bg_jobs(bg_jobs, timeout=None):
mbligha5630a52008-09-03 22:09:50 +0000394 """Joins the bg_jobs with the current thread.
395
396 Returns the same list of bg_jobs objects that was passed in.
397 """
mblighae69f262009-04-17 20:14:56 +0000398 ret, timeout_error = 0, False
mbligh849a0f62008-08-28 20:12:19 +0000399 for bg_job in bg_jobs:
400 bg_job.output_prepare(StringIO.StringIO(), StringIO.StringIO())
mbligh63073c92008-03-31 16:49:32 +0000401
jadmanski0afbb632008-06-06 21:10:57 +0000402 try:
403 # We are holding ends to stdin, stdout pipes
404 # hence we need to be sure to close those fds no mater what
405 start_time = time.time()
mbligh849a0f62008-08-28 20:12:19 +0000406 timeout_error = _wait_for_commands(bg_jobs, start_time, timeout)
407
408 for bg_job in bg_jobs:
409 # Process stdout and stderr
410 bg_job.process_output(stdout=True,final_read=True)
411 bg_job.process_output(stdout=False,final_read=True)
jadmanski0afbb632008-06-06 21:10:57 +0000412 finally:
413 # close our ends of the pipes to the sp no matter what
mbligh849a0f62008-08-28 20:12:19 +0000414 for bg_job in bg_jobs:
415 bg_job.cleanup()
mbligh63073c92008-03-31 16:49:32 +0000416
mbligh849a0f62008-08-28 20:12:19 +0000417 if timeout_error:
418 # TODO: This needs to be fixed to better represent what happens when
419 # running in parallel. However this is backwards compatable, so it will
420 # do for the time being.
421 raise error.CmdError(bg_jobs[0].command, bg_jobs[0].result,
422 "Command(s) did not complete within %d seconds"
423 % timeout)
mbligh63073c92008-03-31 16:49:32 +0000424
mbligh63073c92008-03-31 16:49:32 +0000425
mbligh849a0f62008-08-28 20:12:19 +0000426 return bg_jobs
mbligh63073c92008-03-31 16:49:32 +0000427
mbligh849a0f62008-08-28 20:12:19 +0000428
429def _wait_for_commands(bg_jobs, start_time, timeout):
430 # This returns True if it must return due to a timeout, otherwise False.
431
mblighf0b4a0a2008-09-03 20:46:16 +0000432 # To check for processes which terminate without producing any output
433 # a 1 second timeout is used in select.
434 SELECT_TIMEOUT = 1
435
mbligh849a0f62008-08-28 20:12:19 +0000436 select_list = []
437 reverse_dict = {}
438 for bg_job in bg_jobs:
439 select_list.append(bg_job.sp.stdout)
440 select_list.append(bg_job.sp.stderr)
441 reverse_dict[bg_job.sp.stdout] = (bg_job,True)
442 reverse_dict[bg_job.sp.stderr] = (bg_job,False)
443
jadmanski0afbb632008-06-06 21:10:57 +0000444 if timeout:
445 stop_time = start_time + timeout
446 time_left = stop_time - time.time()
447 else:
448 time_left = None # so that select never times out
449 while not timeout or time_left > 0:
450 # select will return when stdout is ready (including when it is
451 # EOF, that is the process has terminated).
mblighf0b4a0a2008-09-03 20:46:16 +0000452 ready, _, _ = select.select(select_list, [], [], SELECT_TIMEOUT)
mbligh849a0f62008-08-28 20:12:19 +0000453
jadmanski0afbb632008-06-06 21:10:57 +0000454 # os.read() has to be used instead of
455 # subproc.stdout.read() which will otherwise block
mbligh849a0f62008-08-28 20:12:19 +0000456 for fileno in ready:
457 bg_job,stdout = reverse_dict[fileno]
458 bg_job.process_output(stdout)
mbligh63073c92008-03-31 16:49:32 +0000459
mbligh849a0f62008-08-28 20:12:19 +0000460 remaining_jobs = [x for x in bg_jobs if x.result.exit_status is None]
461 if len(remaining_jobs) == 0:
462 return False
463 for bg_job in remaining_jobs:
464 bg_job.result.exit_status = bg_job.sp.poll()
mbligh8ea61e22008-05-09 18:09:37 +0000465
jadmanski0afbb632008-06-06 21:10:57 +0000466 if timeout:
467 time_left = stop_time - time.time()
mbligh63073c92008-03-31 16:49:32 +0000468
mbligh849a0f62008-08-28 20:12:19 +0000469 # Kill all processes which did not complete prior to timeout
470 for bg_job in [x for x in bg_jobs if x.result.exit_status is None]:
mbligh7afc3a62008-11-27 00:35:44 +0000471 print '* Warning: run process timeout (%s) fired' % timeout
mbligh849a0f62008-08-28 20:12:19 +0000472 nuke_subprocess(bg_job.sp)
mbligh095dc642008-10-01 03:41:35 +0000473 bg_job.result.exit_status = bg_job.sp.poll()
mbligh8ea61e22008-05-09 18:09:37 +0000474
mbligh849a0f62008-08-28 20:12:19 +0000475 return True
mbligh63073c92008-03-31 16:49:32 +0000476
477
mbligh63073c92008-03-31 16:49:32 +0000478def nuke_subprocess(subproc):
jadmanski09f92032008-09-17 14:05:27 +0000479 # check if the subprocess is still alive, first
480 if subproc.poll() is not None:
481 return subproc.poll()
482
jadmanski0afbb632008-06-06 21:10:57 +0000483 # the process has not terminated within timeout,
484 # kill it via an escalating series of signals.
485 signal_queue = [signal.SIGTERM, signal.SIGKILL]
486 for sig in signal_queue:
487 try:
488 os.kill(subproc.pid, sig)
489 # The process may have died before we could kill it.
490 except OSError:
491 pass
mbligh63073c92008-03-31 16:49:32 +0000492
jadmanski0afbb632008-06-06 21:10:57 +0000493 for i in range(5):
494 rc = subproc.poll()
mblighd876f452008-12-03 15:09:17 +0000495 if rc is not None:
jadmanski0afbb632008-06-06 21:10:57 +0000496 return rc
497 time.sleep(1)
mbligh63073c92008-03-31 16:49:32 +0000498
499
500def nuke_pid(pid):
jadmanski0afbb632008-06-06 21:10:57 +0000501 # the process has not terminated within timeout,
502 # kill it via an escalating series of signals.
503 signal_queue = [signal.SIGTERM, signal.SIGKILL]
504 for sig in signal_queue:
505 try:
506 os.kill(pid, sig)
mbligh63073c92008-03-31 16:49:32 +0000507
jadmanski0afbb632008-06-06 21:10:57 +0000508 # The process may have died before we could kill it.
509 except OSError:
510 pass
mbligh63073c92008-03-31 16:49:32 +0000511
jadmanski0afbb632008-06-06 21:10:57 +0000512 try:
513 for i in range(5):
514 status = os.waitpid(pid, os.WNOHANG)[0]
515 if status == pid:
516 return
517 time.sleep(1)
mbligh63073c92008-03-31 16:49:32 +0000518
jadmanski0afbb632008-06-06 21:10:57 +0000519 if status != pid:
520 raise error.AutoservRunError('Could not kill %d'
521 % pid, None)
mbligh63073c92008-03-31 16:49:32 +0000522
jadmanski0afbb632008-06-06 21:10:57 +0000523 # the process died before we join it.
524 except OSError:
525 pass
mbligh63073c92008-03-31 16:49:32 +0000526
527
mbligh298ed592009-06-15 21:52:57 +0000528def pid_is_alive(pid):
529 """
530 True if process pid exists and is not yet stuck in Zombie state.
531 Zombies are impossible to move between cgroups, etc.
532 pid can be integer, or text of integer.
533 """
534 try:
mbligh277a0e42009-07-11 00:11:45 +0000535 stat = read_one_line('/proc/%s/stat' % pid) # pid exists
mbligh298ed592009-06-15 21:52:57 +0000536 return stat.split()[2] != 'Z' # and is not in Zombie state
537 except Exception:
538 return False # process no longer exists at all
539
540
mbligh63073c92008-03-31 16:49:32 +0000541def system(command, timeout=None, ignore_status=False):
mbligha5630a52008-09-03 22:09:50 +0000542 """This function returns the exit status of command."""
mblighf8dffb12008-10-29 16:45:26 +0000543 return run(command, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000544 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).exit_status
mbligh63073c92008-03-31 16:49:32 +0000545
546
mbligha5630a52008-09-03 22:09:50 +0000547def system_parallel(commands, timeout=None, ignore_status=False):
548 """This function returns a list of exit statuses for the respective
549 list of commands."""
550 return [bg_jobs.exit_status for bg_jobs in
mblighf8dffb12008-10-29 16:45:26 +0000551 run_parallel(commands, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000552 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligh849a0f62008-08-28 20:12:19 +0000553
554
mbligh8ea61e22008-05-09 18:09:37 +0000555def system_output(command, timeout=None, ignore_status=False,
jadmanski0afbb632008-06-06 21:10:57 +0000556 retain_output=False):
557 if retain_output:
mblighf8dffb12008-10-29 16:45:26 +0000558 out = run(command, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000559 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).stdout
jadmanski0afbb632008-06-06 21:10:57 +0000560 else:
mblighf8dffb12008-10-29 16:45:26 +0000561 out = run(command, timeout=timeout, ignore_status=ignore_status).stdout
jadmanski0afbb632008-06-06 21:10:57 +0000562 if out[-1:] == '\n': out = out[:-1]
563 return out
mbligh63073c92008-03-31 16:49:32 +0000564
mbligh849a0f62008-08-28 20:12:19 +0000565
mbligha5630a52008-09-03 22:09:50 +0000566def system_output_parallel(commands, timeout=None, ignore_status=False,
567 retain_output=False):
568 if retain_output:
showard108d73e2009-06-22 18:14:41 +0000569 out = [bg_job.stdout for bg_job
570 in run_parallel(commands, timeout=timeout,
571 ignore_status=ignore_status,
572 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligha5630a52008-09-03 22:09:50 +0000573 else:
mblighf8dffb12008-10-29 16:45:26 +0000574 out = [bg_job.stdout for bg_job in run_parallel(commands,
575 timeout=timeout, ignore_status=ignore_status)]
mbligha5630a52008-09-03 22:09:50 +0000576 for x in out:
577 if out[-1:] == '\n': out = out[:-1]
578 return out
579
580
mbligh98467952008-11-19 00:25:45 +0000581def strip_unicode(input):
582 if type(input) == list:
583 return [strip_unicode(i) for i in input]
584 elif type(input) == dict:
585 output = {}
586 for key in input.keys():
587 output[str(key)] = strip_unicode(input[key])
588 return output
589 elif type(input) == unicode:
590 return str(input)
591 else:
592 return input
593
594
mbligha5630a52008-09-03 22:09:50 +0000595def get_cpu_percentage(function, *args, **dargs):
596 """Returns a tuple containing the CPU% and return value from function call.
597
598 This function calculates the usage time by taking the difference of
599 the user and system times both before and after the function call.
600 """
601 child_pre = resource.getrusage(resource.RUSAGE_CHILDREN)
602 self_pre = resource.getrusage(resource.RUSAGE_SELF)
603 start = time.time()
604 to_return = function(*args, **dargs)
605 elapsed = time.time() - start
606 self_post = resource.getrusage(resource.RUSAGE_SELF)
607 child_post = resource.getrusage(resource.RUSAGE_CHILDREN)
608
609 # Calculate CPU Percentage
610 s_user, s_system = [a - b for a, b in zip(self_post, self_pre)[:2]]
611 c_user, c_system = [a - b for a, b in zip(child_post, child_pre)[:2]]
612 cpu_percent = (s_user + c_user + s_system + c_system) / elapsed
613
614 return cpu_percent, to_return
615
616
mblighc1cbc992008-05-27 20:01:45 +0000617"""
618This function is used when there is a need to run more than one
619job simultaneously starting exactly at the same time. It basically returns
620a modified control file (containing the synchronization code prepended)
621whenever it is ready to run the control file. The synchronization
622is done using barriers to make sure that the jobs start at the same time.
623
624Here is how the synchronization is done to make sure that the tests
625start at exactly the same time on the client.
626sc_bar is a server barrier and s_bar, c_bar are the normal barriers
627
628 Job1 Job2 ...... JobN
629 Server: | sc_bar
630 Server: | s_bar ...... s_bar
631 Server: | at.run() at.run() ...... at.run()
632 ----------|------------------------------------------------------
633 Client | sc_bar
634 Client | c_bar c_bar ...... c_bar
635 Client | <run test> <run test> ...... <run test>
636
637
638PARAMS:
639 control_file : The control file which to which the above synchronization
640 code would be prepended to
641 host_name : The host name on which the job is going to run
642 host_num (non negative) : A number to identify the machine so that we have
643 different sets of s_bar_ports for each of the machines.
644 instance : The number of the job
645 num_jobs : Total number of jobs that are going to run in parallel with
646 this job starting at the same time
647 port_base : Port number that is used to derive the actual barrier ports.
648
649RETURN VALUE:
650 The modified control file.
651
652"""
653def get_sync_control_file(control, host_name, host_num,
jadmanski0afbb632008-06-06 21:10:57 +0000654 instance, num_jobs, port_base=63100):
655 sc_bar_port = port_base
656 c_bar_port = port_base
657 if host_num < 0:
658 print "Please provide a non negative number for the host"
659 return None
660 s_bar_port = port_base + 1 + host_num # The set of s_bar_ports are
661 # the same for a given machine
mblighc1cbc992008-05-27 20:01:45 +0000662
jadmanski0afbb632008-06-06 21:10:57 +0000663 sc_bar_timeout = 180
664 s_bar_timeout = c_bar_timeout = 120
mblighc1cbc992008-05-27 20:01:45 +0000665
jadmanski0afbb632008-06-06 21:10:57 +0000666 # The barrier code snippet is prepended into the conrol file
667 # dynamically before at.run() is called finally.
668 control_new = []
mblighc1cbc992008-05-27 20:01:45 +0000669
jadmanski0afbb632008-06-06 21:10:57 +0000670 # jobid is the unique name used to identify the processes
671 # trying to reach the barriers
672 jobid = "%s#%d" % (host_name, instance)
mblighc1cbc992008-05-27 20:01:45 +0000673
jadmanski0afbb632008-06-06 21:10:57 +0000674 rendv = []
675 # rendvstr is a temp holder for the rendezvous list of the processes
676 for n in range(num_jobs):
677 rendv.append("'%s#%d'" % (host_name, n))
678 rendvstr = ",".join(rendv)
mblighc1cbc992008-05-27 20:01:45 +0000679
jadmanski0afbb632008-06-06 21:10:57 +0000680 if instance == 0:
681 # Do the setup and wait at the server barrier
682 # Clean up the tmp and the control dirs for the first instance
683 control_new.append('if os.path.exists(job.tmpdir):')
684 control_new.append("\t system('umount -f %s > /dev/null"
685 "2> /dev/null' % job.tmpdir,"
686 "ignore_status=True)")
687 control_new.append("\t system('rm -rf ' + job.tmpdir)")
688 control_new.append(
689 'b0 = job.barrier("%s", "sc_bar", %d, port=%d)'
690 % (jobid, sc_bar_timeout, sc_bar_port))
691 control_new.append(
mbligh9c12f772009-06-22 19:03:55 +0000692 'b0.rendezvous_servers("PARALLEL_MASTER", "%s")'
jadmanski0afbb632008-06-06 21:10:57 +0000693 % jobid)
mblighc1cbc992008-05-27 20:01:45 +0000694
jadmanski0afbb632008-06-06 21:10:57 +0000695 elif instance == 1:
696 # Wait at the server barrier to wait for instance=0
697 # process to complete setup
698 b0 = barrier.barrier("PARALLEL_MASTER", "sc_bar", sc_bar_timeout,
699 port=sc_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000700 b0.rendezvous_servers("PARALLEL_MASTER", jobid)
mblighc1cbc992008-05-27 20:01:45 +0000701
jadmanski0afbb632008-06-06 21:10:57 +0000702 if(num_jobs > 2):
703 b1 = barrier.barrier(jobid, "s_bar", s_bar_timeout,
704 port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000705 b1.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000706
jadmanski0afbb632008-06-06 21:10:57 +0000707 else:
708 # For the rest of the clients
709 b2 = barrier.barrier(jobid, "s_bar", s_bar_timeout, port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000710 b2.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000711
jadmanski0afbb632008-06-06 21:10:57 +0000712 # Client side barrier for all the tests to start at the same time
713 control_new.append('b1 = job.barrier("%s", "c_bar", %d, port=%d)'
714 % (jobid, c_bar_timeout, c_bar_port))
mbligh9c12f772009-06-22 19:03:55 +0000715 control_new.append("b1.rendezvous(%s)" % rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000716
jadmanski0afbb632008-06-06 21:10:57 +0000717 # Stick in the rest of the control file
718 control_new.append(control)
mblighc1cbc992008-05-27 20:01:45 +0000719
jadmanski0afbb632008-06-06 21:10:57 +0000720 return "\n".join(control_new)
mblighc1cbc992008-05-27 20:01:45 +0000721
mbligh63073c92008-03-31 16:49:32 +0000722
mblighc5ddfd12008-08-04 17:15:00 +0000723def get_arch(run_function=run):
724 """
725 Get the hardware architecture of the machine.
726 run_function is used to execute the commands. It defaults to
727 utils.run() but a custom method (if provided) should be of the
728 same schema as utils.run. It should return a CmdResult object and
729 throw a CmdError exception.
730 """
731 arch = run_function('/bin/uname -m').stdout.rstrip()
732 if re.match(r'i\d86$', arch):
733 arch = 'i386'
734 return arch
735
736
showard4745ecd2009-05-26 19:34:56 +0000737def get_num_logical_cpus_per_socket(run_function=run):
mbligh9fd9afe2009-04-28 18:27:25 +0000738 """
739 Get the number of cores (including hyperthreading) per cpu.
740 run_function is used to execute the commands. It defaults to
741 utils.run() but a custom method (if provided) should be of the
742 same schema as utils.run. It should return a CmdResult object and
743 throw a CmdError exception.
744 """
showard4745ecd2009-05-26 19:34:56 +0000745 siblings = run_function('grep "^siblings" /proc/cpuinfo').stdout.rstrip()
746 num_siblings = map(int,
747 re.findall(r'^siblings\s*:\s*(\d+)\s*$',
748 siblings, re.M))
749 if len(num_siblings) == 0:
750 raise error.TestError('Unable to find siblings info in /proc/cpuinfo')
751 if min(num_siblings) != max(num_siblings):
752 raise error.TestError('Number of siblings differ %r' %
753 num_siblings)
754 return num_siblings[0]
mbligh9fd9afe2009-04-28 18:27:25 +0000755
756
jadmanski4f909252008-12-01 20:47:10 +0000757def merge_trees(src, dest):
758 """
759 Merges a source directory tree at 'src' into a destination tree at
760 'dest'. If a path is a file in both trees than the file in the source
761 tree is APPENDED to the one in the destination tree. If a path is
762 a directory in both trees then the directories are recursively merged
763 with this function. In any other case, the function will skip the
764 paths that cannot be merged (instead of failing).
765 """
766 if not os.path.exists(src):
767 return # exists only in dest
768 elif not os.path.exists(dest):
769 if os.path.isfile(src):
770 shutil.copy2(src, dest) # file only in src
771 else:
772 shutil.copytree(src, dest, symlinks=True) # dir only in src
773 return
774 elif os.path.isfile(src) and os.path.isfile(dest):
775 # src & dest are files in both trees, append src to dest
776 destfile = open(dest, "a")
777 try:
778 srcfile = open(src)
779 try:
780 destfile.write(srcfile.read())
781 finally:
782 srcfile.close()
783 finally:
784 destfile.close()
785 elif os.path.isdir(src) and os.path.isdir(dest):
786 # src & dest are directories in both trees, so recursively merge
787 for name in os.listdir(src):
788 merge_trees(os.path.join(src, name), os.path.join(dest, name))
789 else:
790 # src & dest both exist, but are incompatible
791 return
792
793
mbligh63073c92008-03-31 16:49:32 +0000794class CmdResult(object):
jadmanski0afbb632008-06-06 21:10:57 +0000795 """
796 Command execution result.
mbligh63073c92008-03-31 16:49:32 +0000797
jadmanski0afbb632008-06-06 21:10:57 +0000798 command: String containing the command line itself
799 exit_status: Integer exit code of the process
800 stdout: String containing stdout of the process
801 stderr: String containing stderr of the process
802 duration: Elapsed wall clock time running the process
803 """
mbligh63073c92008-03-31 16:49:32 +0000804
805
mblighcd63a212009-05-01 23:04:38 +0000806 def __init__(self, command="", stdout="", stderr="",
jadmanski0afbb632008-06-06 21:10:57 +0000807 exit_status=None, duration=0):
808 self.command = command
809 self.exit_status = exit_status
810 self.stdout = stdout
811 self.stderr = stderr
812 self.duration = duration
mbligh63073c92008-03-31 16:49:32 +0000813
814
jadmanski0afbb632008-06-06 21:10:57 +0000815 def __repr__(self):
816 wrapper = textwrap.TextWrapper(width = 78,
817 initial_indent="\n ",
818 subsequent_indent=" ")
819
820 stdout = self.stdout.rstrip()
821 if stdout:
822 stdout = "\nstdout:\n%s" % stdout
823
824 stderr = self.stderr.rstrip()
825 if stderr:
826 stderr = "\nstderr:\n%s" % stderr
827
828 return ("* Command: %s\n"
829 "Exit status: %s\n"
830 "Duration: %s\n"
831 "%s"
832 "%s"
833 % (wrapper.fill(self.command), self.exit_status,
834 self.duration, stdout, stderr))
mbligh63073c92008-03-31 16:49:32 +0000835
836
mbligh462c0152008-03-13 15:37:10 +0000837class run_randomly:
jadmanski0afbb632008-06-06 21:10:57 +0000838 def __init__(self, run_sequentially=False):
839 # Run sequentially is for debugging control files
840 self.test_list = []
841 self.run_sequentially = run_sequentially
mbligh462c0152008-03-13 15:37:10 +0000842
843
jadmanski0afbb632008-06-06 21:10:57 +0000844 def add(self, *args, **dargs):
845 test = (args, dargs)
846 self.test_list.append(test)
mbligh462c0152008-03-13 15:37:10 +0000847
848
jadmanski0afbb632008-06-06 21:10:57 +0000849 def run(self, fn):
850 while self.test_list:
851 test_index = random.randint(0, len(self.test_list)-1)
852 if self.run_sequentially:
853 test_index = 0
854 (args, dargs) = self.test_list.pop(test_index)
855 fn(*args, **dargs)
mbligha7007722009-01-13 00:37:11 +0000856
857
mblighdd669372009-02-03 21:57:18 +0000858def import_site_symbol(path, module, name, dummy=None, modulefile=None):
859 """
860 Try to import site specific symbol from site specific file if it exists
861
862 @param path full filename of the source file calling this (ie __file__)
863 @param module full module name
864 @param name symbol name to be imported from the site file
865 @param dummy dummy value to return in case there is no symbol to import
866 @param modulefile module filename
867
868 @return site specific symbol or dummy
869
870 @exception ImportError if the site file exists but imports fails
871 """
mbligha7007722009-01-13 00:37:11 +0000872 short_module = module[module.rfind(".") + 1:]
873
874 if not modulefile:
875 modulefile = short_module + ".py"
876
877 try:
878 site_exists = os.path.getsize(os.path.join(os.path.dirname(path),
879 modulefile))
880 except os.error:
881 site_exists = False
882
mbligh61f4e442009-06-08 16:48:20 +0000883 msg = None
mbligha7007722009-01-13 00:37:11 +0000884 if site_exists:
mbligh61f4e442009-06-08 16:48:20 +0000885 # special unique value to tell us if the symbol can't be imported
886 cant_import = object()
887
mbligh062ed152009-01-13 00:57:14 +0000888 # return the object from the imported module
mbligh61f4e442009-06-08 16:48:20 +0000889 obj = getattr(__import__(module, {}, {}, [short_module]), name,
890 cant_import)
891 if obj is cant_import:
892 msg = ("unable to import site symbol '%s', using non-site "
893 "implementation") % name
mbligha7007722009-01-13 00:37:11 +0000894 else:
jadmanski0dc8ff82009-02-11 15:11:15 +0000895 msg = "unable to import site module '%s', using non-site implementation"
896 msg %= modulefile
mbligh61f4e442009-06-08 16:48:20 +0000897
898 if msg:
showardb18134f2009-03-20 20:52:18 +0000899 logging.info(msg)
mbligh062ed152009-01-13 00:57:14 +0000900 obj = dummy
901
902 return obj
903
904
905def import_site_class(path, module, classname, baseclass, modulefile=None):
906 """
907 Try to import site specific class from site specific file if it exists
908
909 Args:
910 path: full filename of the source file calling this (ie __file__)
911 module: full module name
912 classname: class name to be loaded from site file
mbligh0a8c3322009-04-28 18:32:19 +0000913 baseclass: base class object to return when no site file present or
914 to mixin when site class exists but is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +0000915 modulefile: module filename
916
mbligh0a8c3322009-04-28 18:32:19 +0000917 Returns: baseclass if site specific class does not exist, the site specific
918 class if it exists and is inherited from baseclass or a mixin of the
919 site specific class and baseclass when the site specific class exists
920 and is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +0000921
922 Raises: ImportError if the site file exists but imports fails
923 """
924
mblighdd669372009-02-03 21:57:18 +0000925 res = import_site_symbol(path, module, classname, None, modulefile)
mbligh0a8c3322009-04-28 18:32:19 +0000926 if res:
927 if not issubclass(res, baseclass):
928 # if not a subclass of baseclass then mix in baseclass with the
929 # site specific class object and return the result
930 res = type(classname, (res, baseclass), {})
931 else:
932 res = baseclass
mbligha7007722009-01-13 00:37:11 +0000933
mbligh062ed152009-01-13 00:57:14 +0000934 return res
935
936
937def import_site_function(path, module, funcname, dummy, modulefile=None):
938 """
939 Try to import site specific function from site specific file if it exists
940
941 Args:
942 path: full filename of the source file calling this (ie __file__)
943 module: full module name
944 funcname: function name to be imported from site file
945 dummy: dummy function to return in case there is no function to import
946 modulefile: module filename
947
948 Returns: site specific function object or dummy
949
950 Raises: ImportError if the site file exists but imports fails
951 """
952
mblighdd669372009-02-03 21:57:18 +0000953 return import_site_symbol(path, module, funcname, dummy, modulefile)
mblighfb676032009-04-01 18:25:38 +0000954
955
956def write_pid(program_name):
957 """
958 Try to drop <program_name>.pid in the main autotest directory.
959
960 Args:
961 program_name: prefix for file name
962 """
963
964 my_path = os.path.dirname(__file__)
965 pid_path = os.path.abspath(os.path.join(my_path, "../.."))
966 pidf = open(os.path.join(pid_path, "%s.pid" % program_name), "w")
967 if pidf:
968 pidf.write("%s\n" % os.getpid())
969 pidf.close()
mbligh45561782009-05-11 21:14:34 +0000970
971
972def get_relative_path(path, reference):
973 """Given 2 absolute paths "path" and "reference", compute the path of
974 "path" as relative to the directory "reference".
975
976 @param path the absolute path to convert to a relative path
977 @param reference an absolute directory path to which the relative
978 path will be computed
979 """
980 # normalize the paths (remove double slashes, etc)
981 assert(os.path.isabs(path))
982 assert(os.path.isabs(reference))
983
984 path = os.path.normpath(path)
985 reference = os.path.normpath(reference)
986
987 # we could use os.path.split() but it splits from the end
988 path_list = path.split(os.path.sep)[1:]
989 ref_list = reference.split(os.path.sep)[1:]
990
991 # find the longest leading common path
992 for i in xrange(min(len(path_list), len(ref_list))):
993 if path_list[i] != ref_list[i]:
994 # decrement i so when exiting this loop either by no match or by
995 # end of range we are one step behind
996 i -= 1
997 break
998 i += 1
999 # drop the common part of the paths, not interested in that anymore
1000 del path_list[:i]
1001
1002 # for each uncommon component in the reference prepend a ".."
1003 path_list[:0] = ['..'] * (len(ref_list) - i)
1004
1005 return os.path.join(*path_list)
mbligh277a0e42009-07-11 00:11:45 +00001006
1007
1008def sh_escape(command):
1009 """
1010 Escape special characters from a command so that it can be passed
1011 as a double quoted (" ") string in a (ba)sh command.
1012
1013 Args:
1014 command: the command string to escape.
1015
1016 Returns:
1017 The escaped command string. The required englobing double
1018 quotes are NOT added and so should be added at some point by
1019 the caller.
1020
1021 See also: http://www.tldp.org/LDP/abs/html/escapingsection.html
1022 """
1023 command = command.replace("\\", "\\\\")
1024 command = command.replace("$", r'\$')
1025 command = command.replace('"', r'\"')
1026 command = command.replace('`', r'\`')
1027 return command